第十章:IO完成端口
来源:互联网 发布:java实现支付功能代码 编辑:程序博客网 时间:2024/04/29 20:23
1:创建IO完成端口
1:线程数量等于CPU数量时效率最高
2:线程的创建是有开销的,所以应该使用线程池
3:创建函数:
HANDLE CreateIoCompletionPort(HANDLE FileHandle,HANDLE ExistingCompletionPort,ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads//如果传递0,则线程数量=CPU数量);//完成端口是内核对象,但它没有安全描述符
2:将设备与IO完成端口关联起来
1:一般情况下,会将创建IO完成端口和将设备与IO完成端口关联起来两者分离,代码如下
HANDLE hd=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);CreateIoCompletionPort(hFile,hd,dwKey,0);
2:IO完成端口5个数据结构
[1:设备列表]
与完成端口相关联的<设备列表,完成键>对
[2:IO完成队列]
这是一个先进先出队列,当一个异步IO完成后,系统会调用PostQueuedCompletionStatus()投递到此队列末尾
我们可以让一个已完成的异步IO不投递到IO完成队列,比如向一个套接字发送数据,我们不关心是否发送完成,如下代码可以产生这样的效果
Overlapped.hEvent=CreateEvent();Overlapped.hEvent=(HANDLE)((DWORD_PTR)Overlapped.hEvent|1);ReadFile(...,&Overlapped);...;CloseHandle((HANDLE)Overlapped.hEvent&~1);
[插曲]
线程池中的线程调用
BOOL GetQueuedCompletionStatus(HANDLE CompletionPort,LPDWORD lpNumberOfBytes,PULONG_PTR lpCompletionKey,LPOVERLAPPED* lpOverlapped,DWORD dwMilliseconds);
从IO完成队列中获取一个已经完成的异步IO,如果没有,线程会进入睡眠状态,我们应该在一个循环中调用此函数
线程池线程数量经验值是CPU*2
[3:等待线程队列]
此队列是后入先出队列
当线程调用GetQueuedCompletionStatus()后,线程的ThreadID被加入此队列,当IO完成队列出现一项时,此队列中一个线程会被唤醒
之所以使用后入先出队列,是因为如果几个线程长时间未被唤醒,将被唤出内存和CPU cache,节约内存
//等待线程中处理DWORD dwNumBytes;ULONG_PTR CompletionKey;OVERLAPPED* pOverlapped;//初始化BOOL bOK=GetQueuedCompletionStatus(hdCompletionPort,&dwNumBytes,&CompletionKey,&pOverlapped,1000);DWORD dwError=GetLastError();if(bOK){//成功得到IO完成队列中的一项}else{if(pOverlapped=NULL){//IO出现错误,查看dwError确定错误}else{if(dwError==WAIT_TIMEOUT){//超时}else{//查看dwError确定错误}}}
Vista中提供GetQueuedCompletionStatusEx()让一个线程处理多个IO完成队列,这样当负荷非常大时,可以避免频繁的线程切换所带来的开销
当我们对一个完成端口设备发出一个异步IO请求,系统一定会将结果添加到完成端口的IO完成队列中,这样做目的是给程序员提供一个一致的编程模型,我们可以通过调用SetFileCompletionNotificationModes()并传入FILE_SKIP_COMPLETION_PORT_ON_SUCCESS告诉系统不要将以同步方式完成的请求放入IO完成队列中
可以调用SetFileIoOverlappedRange()将OVERLAPPED所用的内存锁定,使其不能被唤出内存,这样可提高性能
[4:已释放线程队列]
[5:已暂停线程队列]
当等待线程队列中个一个线程被唤醒,此线程就进入已释放线程队列,此时已释放线程队列个数不会大于最大运行运行线程数量,当已释放线程队列中一个线程因为调用了一个函数而被挂起,此线程会进入已暂停线程队列,IO完成端口会唤醒一个等待线程队列中的线程,以补满已释放线程队列,这样CPU便能满负荷运行,如果一个线程从已暂停线程队列回到已释放线程队列,此时已释放线程队列中线程数量就会大于允许的最大线程数量,但这个状态不会保持很长时间,因为当线程任务结束后,会回到等待线程队列中去
3:线程池中的线程个数
可以采用启发式算法动态调整
4:说明
vista之前的系统是:当一个线程投递了一些完成端口异步请求,当这些请求未完成时,线程不能被终止,如果强行终止,这些请求会被取消
vista之后,线程终止后,这些请求仍然会被投递到IO完成队列中
5:投递一个模拟的已完成的IO请求
BOOL PostQueuedCompletionStatus( HANDLE CompletionPort, DWORD dwNumberOfBytesTransferred, ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped);
这个函数可以通知所有等待线程应该退出了,为每个线程调用此函数,在线程循环中判断完成键(我的设想,书上写的是判断完成返回值),然后线程退出循环
Vista中,调用CloseHande(完成端口句柄);系统会将所有正在等待GetQueuedCompletionStatus()返回的线程唤醒,并返回FALSE,此时调用GetLastError()==ERROR_INVALID_HANDLE,线程就知道应该退出了
- 第十章:IO完成端口
- 重叠IO-完成端口
- IO完成端口
- IO完成端口
- 重叠IO-完成端口
- IO完成端口
- IO完成端口
- Chapter10-IO完成端口
- IO完成端口
- IO完成端口
- IO完成端口
- IO完成端口
- 深入了解IO完成端口
- 异步io, 完成端口补遗
- IO完成端口学习示例
- IO完成端口不错链接
- winsock IO模型 完成端口
- IO完成端口学习示例
- SAP工作分配
- C++ 单件模式学习笔记
- SAP新建开发类
- uC/OS-II Windows下虚拟的问题
- SAP中新建客制表流程
- 第十章:IO完成端口
- 浅谈公司程序开发流程
- SAP屏幕设计器专题:拖拉控件的强大(一)
- 黑马程序员0822_java基础知识
- SAP屏幕设计器专题:初识设计器(二)
- rman恢复-system表空间恢复
- C++ std::tr1::shared_ptr使用
- SAP屏幕设计器专题:编写控件代码(三)
- Linux常用命令