我遇到了here描述的问题,我被建议使用IO完成端口或线程池。
我已经实现了IO完成,调用PostQueuedCompletionStatus
将任务排入队列,并GetQueuedCompletionStatus
执行下一个任务来执行它。我使用IO完成端口作为多生产者/多用户线程安全的FIFO容器,没有显式锁。这使我可以完全控制线程,因为我可能需要长时间终止一个进程并报告它们。如果没有剩下任务,GetQueuedCompletionStatus
也会等待调用线程。
除了终止之外,线程池适合我的需要:我的任务完成不到一毫秒,但有很多。同时调用QueueUserWorkItem
并让操作系统执行同步和执行更简单。
两种方法在性能方面是否有任何差异?有关我的实施的任何意见吗?
答案 0 :(得分:3)
完成端口旨在避免不必要的上下文切换。当调用GetQueuedCompletionStatus的线程完成处理工作项时,它可以直接调用GetQueuedCompletionStatus继续在其当前CPU时间片内获得更多工作。
@Jonathan - 如果你有阻止调用,那么那些永远几乎不会在拉动工作项的线程上进行。它们应该执行异步(使用Begin / End或* Async调用)或阻塞另一个线程(工作线程池)。这将确保为完成端口提供服务的所有线程实际上都在工作,而不是在其他工作项可用时浪费时间阻塞。
稍作澄清:如果您正在管理自己的线程并调用GetQueuedCompletionStatus,那么您已经创建了自己的完成端口,与IO完成端口以及操作系统用于异步IO调用的关联线程池分开。
答案 1 :(得分:2)
查看您的链接线程,看起来您只是从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可能会导致处理项目的显着延迟,因此,添加的项目突发可能导致池增长超过其需要。