IO 完成端口与线程池 API

发布于 2024-10-08 12:16:30 字数 503 浏览 7 评论 0原文

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

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

除了终止之外,线程池满足我的需求:我的任务完成时间不到一毫秒,但任务数量很多。另外,调用 QueueUserWorkItem 并让操作系统进行同步和执行也更简单。

两种方法在性能方面有什么区别吗?对我的实施有什么意见吗?

I had a problem described here, and I was suggested to use IO completion ports or thread pool.

I have implemented IO completion, calling PostQueuedCompletionStatus to enqueue the task and GetQueuedCompletionStatus to get the next task to execute it. I am using IO completion port as a multi-producer/multi-consumer thread-safe FIFO container without explicit locks. This makes me have full control over the threads, because I may need to terminate the ones process for a long time and report them. Also GetQueuedCompletionStatus waits the calling thread if there is no task left.

Aside from the termination, thread pool suits my needs: my tasks are completed less than a millisecond, but there are lots of them. Also calling QueueUserWorkItem and letting the OS do the synchronizations and executions is simpler.

Are there any differences between two approaches performance-wise? Any comments about my implementation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

内心荒芜 2024-10-15 12:16:30

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

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

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

Completion Ports are designed to avoid unnecessary context switching. When your thread that is calling GetQueuedCompletionStatus is done processing a work item, it can call right back to GetQueuedCompletionStatus to continue to get more work within its current CPU timeslice.

@Jonathan - If you have blocking calls, then those should pretty much never be made on a thread pulling work items. They should either be performed async (with a Begin/End or *Async call) or block on another thread (worker thread pool). This will ensure that all of your threads servicing the completion port are actually doing work instead of wasting time blocking when other work items are available.

A slight clarification: if you are managing your own threads and calling GetQueuedCompletionStatus, then you have created your own completion port separate from the IO completion port and associated thread pool used by the OS for async IO calls.

囍孤女 2024-10-15 12:16:30

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

查看链接的线程,看起来您只是从 FIFO 列表中出队任务,与 IO 无关。因此,后者很可能就是您所追求的。我认为性能差异不应该是您关心的问题,而不是哪个 API 适合您正在做的事情。

编辑:如果您需要完全控制线程的创建和终止(尽管终止线程永远都不行,因为堆栈不会展开),那么您最好使用 WaitForSingleObject (或者更确切地说,用于退出信号的 MultipleObjects )创建自己的线程池以及)和 SetEvent。 WinAPI的线程池基本上是微软根据线程的负载自动创建和终止线程。

IO Completion Port (IOCP) is typically used with a threadpool to handle IO events/activities while the WinAPI threadpool (which you indicate via QueueUserWorkItem) is merely Microsoft's implementation of a typical threadpool that will handle non-IO tasks.

Looking at your linked thread, it looks like you're simply dequeing a task from a FIFO list which has nothing to do with IO. So, the latter is most likely what you are after. I don't think performance difference should be your concern here as opposed to which is the right API for what you're doing.

EDIT: If you need full control over thread creation and termination (although it's never OK to terminate a thread as the stack will not unwind), then you'll be better off creating your own threadpool by using WaitForSingleObject (or rather MultipleObjects for exit signal as well) and SetEvent. WinAPI's threadpool is basically Microsoft's automatic thread creation and termination depending on the load of the threads.

婴鹅 2024-10-15 12:16:30

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

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

If you use IO completion ports, and create your own X threads which call GetQueuedCompletionStatus(), and you have X tasks that takes a long time (say, read from network), then all threads would be busy and further requests will starve. AFAIU, thread pool will spin another thread in this case.

Also, never use TerminateThread()! When allocating memory from the heap, a thread temporarily acquire that heap's CRITICAL_SECTION. So if a thread is terminated in the middle of this, other threads trying to allocate from the same heap will hang. And, there's no way for you to know whether the thread is allocating or not.

陌伤浅笑 2024-10-15 12:16:30

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

实际的区别是 - QueueUserWorkItem 在线程池中创建(并管理)线程。因此,它预先不知道需要多少个线程:它从几个(可能只有一个)开始,然后如果线程池没有空闲线程来处理排队项目,则每隔一定时间创建额外的池线程。

这意味着,如果在池较小时添加许多项目,则使用 QueueUserWorkItem 可能会导致处理项目的显着延迟,因此,添加项目的突发可能会导致池变得比所需的更大。

The difference between QueueUserWorkItem and IOCompletionPorts is QueueUserWorkItem is an easy to use higher level abstraction.
Its easy to see how QueueUserWorkItem could be implemented on top of IOCompletionPorts (I don't know that it is).

The practical difference is - QueueUserWorkItem creates (and manages) threads in the thread pool. As such, it doesn;t know up front how many threads it will need: It starts with a few( perhaps just one) and then creates additional pool threads at intervals if the thread pool has no free threads to handle queued items.

This means that using QueueUserWorkItem can result in a significant delay to process items if many items are added while the pool is small, consequently, bursts of items being added can cause the pool to grow larger than it needs to.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文