IOCP是什么就不用介绍了,为什么要用IOCP就更不用提及。这里我们只简单讨论IOCP开发的一个思路,即能提高性能又能隆低开发复杂性。
“即能提高性能又能隆低开发复杂性”?觉得我说的有矛盾吗?不是复杂的代码才能换来高效吗?其实不一定,我认为简单是一切事务的根本,就像遗传学的四个规则就造就了地球千变万化的生命世界。
我们通常看到网上的代码示例都是一个IOCP句柄,多个IO线程和多个处理线程,由于是多线程环境,只有多个线程可能同时访问到的数据,就一定要加锁,那么问题复杂性就上升了:会不会哪个数据忘了加锁;会不会死锁;效率大大降低(降低了多CPU的优势)。
好,现在我们就把问题简单化。比如我现在要写一个服务器,我的设计只包含两个线程,一个是accept线程,一个是IOCP线程。伪代码如下:
Acceptor我们就不用管了,就是开一个线程,等待客户端的连接;IoThread是一个IOCP的线程对象,只开了一个线程,Acceptor和主线程不直接操作IoThread的数据,而是POST到IOCP,例如上面的OnLoadConfig由主线程调用,AddConnection由Acceptor线程调用,两者都是异步的POST。IOCP收到这些请求会做分发,代码中的DoXXX就是真正的处理函数。因为IOCP线程只有 个,那么对config,clients这些数据的处理就不需要加锁了,这里的编程就是单线程编程(虽然里面有异步)。
其实还能做进一步的简化:取消Acceptor线程,因为IOCP本来就能实现异步的Accept。
好了我们现在只有主线程和一个IOCP线程了,主线程只做初始化工作,之后就休息了,一个单线程的程序,写起来多么简单呀!
“即能提高性能又能隆低开发复杂性”?觉得我说的有矛盾吗?不是复杂的代码才能换来高效吗?其实不一定,我认为简单是一切事务的根本,就像遗传学的四个规则就造就了地球千变万化的生命世界。
我们通常看到网上的代码示例都是一个IOCP句柄,多个IO线程和多个处理线程,由于是多线程环境,只有多个线程可能同时访问到的数据,就一定要加锁,那么问题复杂性就上升了:会不会哪个数据忘了加锁;会不会死锁;效率大大降低(降低了多CPU的优势)。
好,现在我们就把问题简单化。比如我现在要写一个服务器,我的设计只包含两个线程,一个是accept线程,一个是IOCP线程。伪代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class
Acceptor
{
public
:
};
class
IoThread
{
public
:
int
InitIoService();
// 创建IPCP,只启动一个线程
int
OnLoadConfig(
const
Config& config);
// 调用PostQueuedCompletionStatus
int
AddConnection(ClientConnection* conn);
// 调用PostQueuedCompletionStatus
private
:
static
unsigned ThreadEntry(
void
*);
unsigned ThreadProc();
// 调用GetQueuedCompletionStatus接收PostQueuedCompletionStatus或IO的消息
// PostQueuedCompletionStatus调用DoXXX处理,IO的消息调用IO处理函数
void
DoLoadConfig(
const
Config& config);
void
DoAddConnection(SOCKET client);
void
OnReadComplite(
int
connId, unsigned length);
void
OnWriteComplite(
int
connId);
private
:
HANDLE
_ioservice;
Config config;
std::map<
int
, ClientConnection*> clients;
};
|
Acceptor我们就不用管了,就是开一个线程,等待客户端的连接;IoThread是一个IOCP的线程对象,只开了一个线程,Acceptor和主线程不直接操作IoThread的数据,而是POST到IOCP,例如上面的OnLoadConfig由主线程调用,AddConnection由Acceptor线程调用,两者都是异步的POST。IOCP收到这些请求会做分发,代码中的DoXXX就是真正的处理函数。因为IOCP线程只有 个,那么对config,clients这些数据的处理就不需要加锁了,这里的编程就是单线程编程(虽然里面有异步)。
其实还能做进一步的简化:取消Acceptor线程,因为IOCP本来就能实现异步的Accept。
好了我们现在只有主线程和一个IOCP线程了,主线程只做初始化工作,之后就休息了,一个单线程的程序,写起来多么简单呀!
那么你可能要问了,机子CPU有N个核,单线程?哪有什么高效性!别急,只稍加改动即可。比如你的CPU有N个核,那么主程序创建N个IoThread对象不就得了,每个IoThread对象都有一份独立的数据,如上例中的config,clients。多个IoThread对象没有增加复杂性。写代码的时候仍然只需要单线程编程的思路。
http://bbs.youkuaiyun.com/topics/370130831