IO完成端口与线程池API

时间:2010-12-22 14:30:40

标签: c++ multithreading winapi threadpool

我遇到了here描述的问题,我被建议使用IO完成端口或线程池。

我已经实现了IO完成,调用PostQueuedCompletionStatus将任务排入队列,并GetQueuedCompletionStatus执行下一个任务来执行它。我使用IO完成端口作为多生产者/多用户线程安全的FIFO容器,没有显式锁。这使我可以完全控制线程,因为我可能需要长时间终止一个进程并报告它们。如果没有剩下任务,GetQueuedCompletionStatus也会等待调用线程。

除了终止之外,线程池适合我的需要:我的任务完成不到一毫秒,但有很多。同时调用QueueUserWorkItem并让操作系统执行同步和执行更简单。

两种方法在性能方面是否有任何差异?有关我的实施的任何意见吗?

4 个答案:

答案 0 :(得分:3)

完成端口旨在避免不必要的上下文切换。当调用GetQueuedCompletionStatus的线程完成处理工作项时,它可以直接调用GetQueuedCompletionStatus继续在其当前CPU时间片内获得更多工作。

@Jonathan - 如果你有阻止调用,那么那些永远几乎不会在拉动工作项的线程上进行。它们应该执行异步(使用Begin / End或* Async调用)或阻塞另一个线程(工作线程池)。这将确保为完成端口提供服务的所有线程实际上都在工作,而不是在其他工作项可用时浪费时间阻塞。

稍作澄清:如果您正在管理自己的线程并调用GetQueuedCompletionStatus,那么您已经创建了自己的完成端口,与IO完成端口以及操作系统用于异步IO调用的关联线程池分开。

答案 1 :(得分:2)

IO完成端口(IOCP)通常与线程池一起用于处理IO事件/活动,而WinAPI线程池(通过QueueUserWorkItem指示)仅仅是Microsoft对将处理非IO任务的典型线程池的实现。

查看您的链接线程,看起来您只是从FIFO列表中取消任务,该列表与IO无关。所以,后者最有可能是你追求的。我不认为性能差异应该是您关注的问题,而不是您正在做的事情的正确API。

编辑:如果你需要完全控制线程的创建和终止(尽管终止线程永远不会因为堆栈不会解开),那么你最好通过使用WaitForSingleObject(或者更确切地说是MultipleObjects)来创建自己的线程池。对于退出信号)和SetEvent。 WinAPI的线程池基本上是Microsoft的自动线程创建和终止,具体取决于线程的负载。

答案 2 :(得分:1)

如果你使用IO完成端口,并创建自己的调用GetQueuedCompletionStatus()的X线程,并且你有X任务需要很长时间(比如从网络读取),那么所有线程都会忙,所以进一步的请求将会饿死。在这种情况下,AFAIU,线程池将旋转另一个线程。

另外,永远不要使用TerminateThread()!从堆中分配内存时,线程会临时获取该堆的CRITICAL_SECTION。因此,如果线程在此中间终止,则尝试从同一堆分配的其他线程将挂起。并且,您无法知道线程是否正在分配。

答案 3 :(得分:0)

QueueUserWorkItem和IOCompletionPorts之间的区别是QueueUserWorkItem是一个易于使用的更高级别的抽象。 很容易看出QueueUserWorkItem如何在IOCompletionPorts之上实现(我不知道它是什么)。

实际的区别是 - QueueUserWorkItem在线程池中创建(和管理)线程。因此,它不知道它将需要多少线程:它从一些(可能只是一个)开始,然后如果线程池没有空闲线程来处理排队的项目,则会间隔创建其他池线程。

这意味着如果在池较小时添加许多项目,使用QueueUserWorkItem可能会导致处理项目的显着延迟,因此,添加的项目突发可能导致池增长超过其需要。