完成端口

完成端口,详情请见: http://blog.csdn.net/ithzhang/article/details/8508161

HANDLE CreateIoCompletionPort(
      HANDLE hFile,
      HANDLE hExistingCompletionPort,
      ULONG_PTR CompletionKey,
      DWORD dwNumberOfConcurrentThreads);

该函数主要完成两件事:

1、创建一个完成端口

2、将设备与一个IO完成端口关联起来

hFile: 就是设备句柄

hExistingCompletionPort:  是与设备相关联的完成端口句柄,当传入NULL时,将创建一个全新的完成端口。

CompletionKey: 是一个用户关心的值,但是系统并不关心传入的是什么。主要用于区分是设备的,如可以传入:READ_KEY、WRITE_KEY、BW_FILE等,同样也可以类似于线程函数中的参数,传入一个this指针,在外面拿到这个指针进行强转,就可以得到对应的类/变量等。

dwNumberOfConcurrentThreads: 告诉操作系统,共有多少个线程并发。如果传入的参数为0,那么取的是默认值,即CPU个数。


因此CreateIoCompletionPort()可以抽象成以下两个函数:

</pre><p></p><p></p><pre name="code" class="cpp">// 创建一个新的完成端口
HANDEL CreateNewCompletionPort(DWORD dwNumberOfConcurrentThreads)
{
	return (CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, dwNumberOfConcurrentThreads));
}

// 将设备与IO完成端口关联起来
BOOL AssociateDeviceWithCompletionPort(HANDLE hCompletionPort, HANDLE hDevice, DWORD dwCompletionKey)
{
	HANDLE h = CreateIoCompletionPort(hDevice, hCompletionPort, dwCompletionKey, 0);
	returun (h == hCompletionPort);
}

CreateIoCompletionPort()相关的几个数据结构:

1、设备列表

每次调用CreateIoCompletionPort()时,系统会判断传入的hExistingCompletionPort是否为NULL, 如果为NULL则重新创建一个IO完成端口。然后为些完成端口创建设备列表,并将设备(hFile)添加到设备列表中(先进先出)


2、IO完成队列

当设备的一个异步IO操作完成后,系统会去检测该设备是否与一个IO完成端口有关联;如果有关联,系统会将这个已完成的IO操作添加到完成端口的完成队列中去。IO完成队列,每一项包括:已传输的字节数、关键项CompletionKey(通过关键项可以拿到是谁完成了操作,可以强转此参数)、以及一个指向IO指求的OVERLAPPED结构指针及错误码。


insert队列条件: I/O请求完成时,调用PostQueuedCompletionStatus()。

delete队列条件:完成端口从等待队列中删除一项时。


Windows为完成端口提供了一个函数,该函数可以将线程切换到睡眠状态,来等待设备IO操作完成并进行完成端口。该函数是阻塞的。

BOOL GetQueuedCompletionStatus(
     HANDLE hCompletionPort,
     PDWORD pdwNumberOfBytesTransferred,
     ULONG_PTR pCompletionKey,
     OVERLAPPED** ppOverlapped,
     DWORD dwMilliSeconds);


hCompeltionPort: 表示线程希望对哪个完成端口进行监测。 GetQueuedCompletionStatus()的任务就是将调用线程切换到睡眠状态,也就是阻塞在此函数上,直到指定的完成端口出现一项完成或是超时。

pdwNumberOfBytesTransfered: 返回在异常IO操作完成时已传输的字节数。

pCompletionKey: 返回完成键,通过强转可以到用户关心的类/成员。

ppOverlapped: 返回异步IO操作开始时,传入的Overlapped结构地址。

dwMilliSeconds: 等待时间。

该函数成功反回True,否则返回False


3、等待线程队列

当线程池中的每个线程调用GetQueuedCompletionStatus()时,调用线程的标识符就会被添加到这个等待线程队列中,这使得IO完成端口知道有哪些线程当前正在等待对已完成的IO请求进行处理。

当IO完成端口的IO完成队列中出现一项时,完成端口会唤醒等待队列中的一个线程。这个线程会得到已完成IO项的所有信息,包括:已传输字节数、完成键、OVERLAPPED结构地址。这些信息是通过GetQueueCompletionStatus()的参数返回得到的。


insert队列条件: 线程调用GetQueuedCompletionStatus()

delete队列条件:IO完成队列不为空,且正在运行的线程数小于最大允许并发数(通常为CPU数量)


IO完成队列的各项是先进先出,但是唤醒等待线程队列中的线程是按照后进先出的方式进行。假设有四个线程正在等待队列中,如果出现一个完成项,那么四个等待线程中最后调用GetQueuedCompletionStatus()的那个线程将会被唤醒来处理这一项。当处理完这一项后,线程会由于再次调用GetQueuedCompletionStatus()而进入到等待队列中。

使用这种算法,就可以得到哪些线程是长时间没有工作,长时间睡眠的。


4、已释放线程队列

存储着已被唤醒的线程句柄。完成端口在等待线程队列中唤醒一个线程,或是已暂停的线程被唤醒时。

insert队列条件:1、完成端口在等待线程队列中唤醒一个线程。 2、已暂停的线程再度被唤醒

delete队列条件:1、线程再次调用GetQueueCompletionStatus() 进入等待线程队列。 2、线程调用一个函数(如Sleep, WaitForSingleObject)等自己挂起,进入已暂停线程队列


5、已暂停线程队列

已释放的线程调用一个函数将自己挂起时,会将此线程ID插入到已暂停线程队列中。

insert队列条件: 已释放的线程调用一个函数将自己挂起

delete队列条件:已挂的线程再度被唤醒,进入已释放线程队列



总的来说,各线程队列转换关系如下:

3delete --> 4

4delete --> 3

4delete --> 5

5delete --> 4



模拟已完成的IO请求

BOOL PostQueuedCompletionStatus(
     HANDLE hCompletionPort,
     DWORD dwNumBytes,
     ULONG_PTR CompletionKey,
     OVERLAPPED*pOverlapped);
这个函数用来将一个已完成的IO通知追加到IO完成端口的队列中。

hCompletionPort:表示我们要将该项加到哪个完成端口的队列中。

剩下的3个参数表示应该返回什么值给那个调用了GetQueuedCompletionStatus()的线程。

当线程从IO完成队列中得到一个模拟项的时候,GetQueuedCompletionStatus()会返回TRUE,表求IO请求成功执行。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值