完成端口 - 图文(4)

2019-09-01 11:36

呵呵,看到CreateIoCompletionPort()的参数不要奇怪,参数就是一个INVALID,一个NULL,两个0…,说白了就是一个-1,三个0……简直就和什么都没传一样,但是Windows系统内部却是好一顿忙活,把完成端口相关的资源和数据结构都已经定义好了(在后面的原理部分我们会看到,完成端口相关的数据结构大部分都是一些用来协调各种网络I/O的队列),然后系统会给我们返回一个有意义的HANDLE,只要返回值不是NULL,就说明建立完成端口成功了,就这么简单,不是吗?

有的时候我真的很赞叹Windows API的封装,把很多其实是很复杂的事整得这么简单……

至于里面各个参数的具体含义,我会放到后面的步骤中去讲,反正这里只要知道创建我们唯一的这个完成端口,就只是需要这么几个参数。

但是对于最后一个参数 0,我这里要简单的说两句,这个0可不是一个普通的0,它代表的是NumberOfConcurrentThreads,也就是说,允许应用程序同时执行的线程数量。当然,我们这里为了避免上下文切换,最理想的状态就是每个处理器上只运行一个线程了,所以我们设置为0,就是说有多少个处理器,就允许同时多少个线程运行。

因为比如一台机器只有两个CPU(或者两个核心),如果让系统同时运行的线程多于本机的CPU数量的话,那其实是没有什么意义的事情,因为这样CPU就不得不在多个线程之间执行上下文切换,这会浪费宝贵的CPU周期,反而降低的效率,我们要牢记这个原则。

【第二步】根据系统中CPU核心的数量建立对应的Worker线程

我们前面已经提到,这个Worker线程很重要,是用来具体处理网络请求、具体和客户端通信的线程,而且对于线程数量的设置很有意思,要等于系统中CPU的数量,那么我们就要首先获取系统中CPU的数量,这个是基本功,我就不多说了,代码如下: [cpp] view plaincopyprint?

1. SYSTEM_INFO si; 2. GetSystemInfo(&si); 3.

4. int m_nProcessors = si.dwNumberOfProcessors;

SYSTEM_INFO si; GetSystemInfo(&si); int m_nProcessors = si.d 这样我们根据系统中CPU的核心数量来建立对应的线程就好了,下图是在我的 i7 2600k CPU上初始化的情况,因为我的CPU是8核,一共启动了16个Worker线程,如下图所示 啊,等等!各位没发现什么问题么?为什么我8核的CPU却启动了16个线程?这个不是和我们第二步中说的原则自相矛盾了么? 哈哈,有个小秘密忘了告诉各位了,江湖上都流传着这么一个公式,就是: 我们最好是建立CPU核心数量*2那么多的线程,这样更可以充分利用CPU资源,因为完成端口的调度是非常智能的,比如我们的Worker线程有的时候可能会有Sleep()或者WaitForSingleObject()之类的情况,这样同一个CPU核心上的另一个线程就可以代替这个Sleep的线程执行了;因为完成端口的目标是要使得CPU满负荷的工作。 这里也有人说是建立 CPU“核心数量 * 2 +2”个线程,我想这个应该没有什么太大的区别,我就是按照我自己的习惯来了。 然后按照这个数量,来启动这么多个Worker线程就好可以了,接下来我们开始下一个步骤。

什么?Worker线程不会建? …囧…

Worker线程和普通线程是一样一样一样的啊~~~,代码大致上如下: [cpp] view plaincopyprint?

1. // 根据CPU数量,建立*2的线程 2. m_nThreads = 2 * m_nProcessors;

3. HANDLE* m_phWorkerThreads = new HANDLE[m_nThreads]; 4.

5. for (int i = 0; i < m_nThreads; i++) 6. {

7. m_phWorkerThreads[i] = ::CreateThread(0, 0, _WorkerThread, …); 8. }

// 根据CPU数量,建立*2的线程 m_nThreads = 2 * m_nProcess HANDLE* m_phWorkerThreads = for (int i = 0; i < m_nThrea { 其中,_WorkerThread是Worker线程的线程函数,线程函数的具体内容我们后面再讲。 【第三步】创建一个用于监听的Socket,绑定到完成端口上,然后开始在指定的端口上监听连接请求 最重要的完成端口建立完毕了,我们就可以利用这个完成端口来进行网络通信了。 首先,我们需要初始化Socket,这里和通常情况下使用Socket初始化的步骤都是一样的,大约就是如下的这么几个过程(详情参照我代码中的LoadSocketLib()和InitializeListenSocket(),这里只是挑出关键部分): [cpp] view plaincopyprint?

1. // 初始化Socket库 2. WSADATA wsaData;

3. WSAStartup(MAKEWORD(2,2), &wsaData); 4. //初始化Socket

5. struct sockaddr_in ServerAddress;

6. // 这里需要特别注意,如果要使用重叠I/O的话,这里必须要使用WSASocket来

初始化Socket

7. // 注意里面有个WSA_FLAG_OVERLAPPED参数

8. SOCKET m_sockListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,

WSA_FLAG_OVERLAPPED); 9. // 填充地址结构信息

10. ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress)); 11. ServerAddress.sin_family = AF_INET;

12. // 这里可以选择绑定任何一个可用的地址,或者是自己指定的一个IP地址 13. //ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY); 14. ServerAddress.sin_addr.s_addr = inet_addr(“你的IP”); 15. ServerAddress.sin_port = htons(11111); 16. // 绑定端口

17. if (SOCKET_ERROR == bind(m_sockListen, (struct sockaddr *) &ServerAddre

ss, sizeof(ServerAddress))) 18. // 开始监听

19. listen(m_sockListen,SOMAXCONN))

// 初始化Socket库 WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &w //初始化Socket struct sockaddr_in ServerAdd // 这里需要特别注意,如果要使用 需要注意的地方有两点: (1) 想要使用重叠I/O的话,初始化Socket的时候一定要使用WSASocket并带上WSA_FLAG_OVERLAPPED参数才可以(只有在服务器端需要这么做,在客户端是不需要的); (2) 注意到listen函数后面用的那个常量SOMAXCONN了吗?这个是在微软在WinSock2.h中定义的,并且还附赠了一条注释,Maximum queue length specifiable by listen.,所以说,不用白不用咯^_^

接下来有一个非常重要的动作:既然我们要使用完成端口来帮我们进行监听工作,那么我们一定要把这个监听Socket和完成端口绑定才可以的吧: 如何绑定呢?同样很简单,用 CreateIoCompletionPort()函数。

等等!大家没觉得这个函数很眼熟么?是的,这个和前面那个创建完成端口用的居然是同一个API!但是这里这个API可不是用来建立完成端口的,而是用于将Socket和以前创建的那个完成端口绑定的,大家可要看准了,不要被迷惑了,因为他们的参数是明显不一样的,前面那个的参数是一个-1,三个0,太好记了…

说实话,我感觉微软应该把这两个函数分开,弄个 CreateNewCompletionPort() 多好呢?

这里在详细讲解一下CreateIoCompletionPort()的几个参数: [cpp] view plaincopyprint?

1. HANDLE WINAPI CreateIoCompletionPort(

2. __in HANDLE FileHandle, // 这里当然是连入的这个套接字句柄

3. __in_opt HANDLE ExistingCompletionPort, // 这个就是前面创建的那个完成

端口

4. __in ULONG_PTR CompletionKey, // 这个参数就是类似于线程参数一

样,在

5. // 绑定的时候把自己定义的结构体指针传递 6. // 这样到了Worker线程中,也可以使用这个 7. // 结构体的数据了,相当于参数的传递 8. __in DWORD NumberOfConcurrentThreads // 这里同样置0 9. );

HANDLE WINAPI CreateIoComp __in HANDLE FileH __in_opt HANDLE Exis __in ULONG_PTR Co


完成端口 - 图文(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:铁路施工企业薪酬管理

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: