【问题标题】:How do I run a large number of blocking operations in a performant way?如何以高效的方式运行大量阻塞操作?
【发布时间】:2021-12-27 11:12:45
【问题描述】:

我想执行一个多次执行阻塞 I/O 操作(最多 67840 次调用)的库方法。该库不提供该方法的异步版本。

由于在大多数情况下调用只是等待超时,我想并行运行多个调用。我的方法是异步的,所以如果我能await 结果就好了。

由于 ThreadPool 不应该用于阻塞操作,我想执行以下操作:

  • 启动多个线程(例如 1024)
  • 在这些线程上运行阻塞调用
  • await 完成(例如通过TaskCompletionSource)并在TheadPool 上的正常任务中处理每个调用的结果

.NET 中是否有现有的类可以用来实现这样的目标?我知道TaskCreationOptions.LongRunning,但据我所知,这将为每个调用创建一个新线程。

【问题讨论】:

  • 我不想告诉你,但是...“由于调用主要等待 I/O 操作完成,我想并行运行多个调用。”...运行多个操作并行不会增加可用的 IO。这正是并行调用无法使事情变得更快的场景,除非您调用一个时间确实可以并行扩展的 API(因为另一个调用使用,即后端的另一个线程/系统)。但是,如果您受 IO 限制,并行性实际上可能会使事情变得更慢。
  • 我很抱歉,但您已经超时的事实表明 IO 过载。向问题抛出更多 IO 看起来并不是一个聪明的主意。
  • 我怀疑框架对此有什么作用,因为它不是预期用途。对于 IO,您使用非阻塞 IO 调用,对于 CPU 工作,您不需要这么多线程。我的意思是在你的情况下你可能别无选择,但框架为这种情况提供解决方案会很奇怪。
  • @TomTom 假设您需要执行 10 个 Web 请求,但无论出于何种原因,您都不能使用异步 IO。然后并行执行肯定会比一个接一个地发出这些请求更快。
  • 抱歉,问题涉及 IO 限制。对(可扩展)API 的 10 个 Web 请求没有 IO 限制。网络扫描不受设计的 IO 限制(带宽很大,ping/icmp 数据包非常小),所以我评论的问题在描述中是 TOTALLY OFF,就这么简单。

标签: c# .net multithreading async-await .net-4.7.2


【解决方案1】:

阻塞 I/O 操作...该库不提供该方法的异步版本。

仅从这一点,您就知道您最终不会得到“理想”的解决方案。理想情况下,I/O 是异步执行的。事实上,在 Windows 上,所有 I/O在操作系统级别异步执行,每个同步 API 调用只是阻塞当前线程,直到异步操作完成。 p>

所以,你应该接受的第一件事是你需要稍微改变规则。

由于在大多数情况下调用只是等待超时,我想并行运行多个调用。

是的。并行是一个合适的解决方案。如果可以异步执行 I/O,那么并行性将不是适当的解决方案,但由于 I/O 是阻塞的(并且您无法控制),那么并行性就是最好的解决方案。

我的方法是异步的,所以如果我能等待结果就好了。

这不一定遵循。只要有明确的记录,异步方法部分阻塞是可以接受的。异步签名(即“返回Task”并具有*Async 后缀)暗示方法可能是异步的,而不是它必须是异步的。

个人,I prefer not to do thread offloading in my logic methods, and only do it when calling them from the UI layer(链接到我的博客)。

因为线程池不应该用于阻塞操作

嗯,这是您可以考虑弯曲的规则之一。线程池确实可以很好地处理阻塞操作,事实上这是我建议的第一个解决方案。

启动多个线程(例如 1024)...在这些线程上运行阻塞调用

如果您放弃“我想要我自己的线程”部分而只使用线程池,那么答案很简单:Parallel 或 PLINQ 会很好地工作。您可以为这两种方法设置最大并行度,并且可以在线程池上设置大于正常的最小线程数,以便根据需要更快地增加线程数。

这确实会在线程池上折腾很多阻塞工作,通常不建议这样做,但在某些情况下可以工作。具体来说,控制台应用程序或 GUI 应用程序等客户端应用程序可以正常工作。但是,如果这是在 Web 应用程序中,那么您不希望用阻塞调用填满线程池。在这种情况下,我实际上建议使用basic distributed architecture(链接到我的博客)将扫描拆分到一个单独的应用程序。

等待完成(例如通过 TaskCompletionSource)并在 TheadPool 上的正常任务中处理每个调用的结果

如果你想在一个单独的线程上做并行工作,那么你可以把它包裹在await Task.Run(...)中;没有必要乱搞 TCS。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多