我如何 运行 以高效的方式进行大量阻塞操作?
How do I run a large number of blocking operations in a performant way?
我想执行一个多次执行阻塞 I/O 操作的库方法(最多 67840 次调用)。该库不提供该方法的异步版本。
由于在大多数情况下调用只是等待超时,我想 运行 并行调用多个。我的方法是异步的,因此如果我能 await
结果就好了。
由于 ThreadPool
不应该用于阻塞操作,我想执行以下操作:
- 启动多个线程(例如1024)
- 运行 这些线程上的阻塞调用
await
完成(例如通过 TaskCompletionSource
)并处理 TheadPool
上正常任务中每次调用的结果
.NET 中是否存在 类,我可以用它来实现这样的目标?我知道 TaskCreationOptions.LongRunning
,但据我所知,这将为每次调用创建一个新线程。
blocking I/O operation... The library does not provide an async version of the method.
仅此一点,您就知道您最终不会得到“理想”的解决方案。理想情况下,I/O 是异步执行的。事实上,在 Windows 上,all I/O is 在 OS 级别异步执行,每个同步 API 调用只会阻塞当前线程,直到异步操作完成。
因此,您应该接受的第一件事是您需要稍微改变一下规则。
Since in most cases the call just waits for a timeout, I want to run multiple calls in parallel.
是的。并行是一个合适的解决方案。如果可以异步执行 I/O,那么并行性 不是 是合适的解决方案,但是由于 I/O 正在阻塞(并且您无法控制那),那么并行性就是你剩下的最佳解决方案。
My method is async, therefore it would be good if I could await the result.
这不一定跟得上。异步方法部分阻塞是可以接受的,只要明确记录即可。异步签名(即“returns a Task
”并具有 *Async
后缀)意味着方法 可能 是异步的,而不是它必须 是异步的。
就个人而言,I prefer not to do thread offloading in my logic methods, and only do it when calling them from the UI layer(link 我的博客)。
Since the ThreadPool should not be used for blocking operations
好吧,这是您可以考虑遵守的规则之一。线程池确实可以很好地处理阻塞操作,实际上这是我建议的第一个解决方案。
Start a number of threads (e.g. 1024)... Run the blocking calls on these threads
如果您抛开“我想要自己的线程”部分而只使用线程池,那么答案很简单:Parallel
或 PLINQ 会工作得很好。您可以为这两种方法设置最大并行度,并且可以在线程池上设置比正常最小线程数更大的线程数,以便根据需要更快地增加线程数。
这样确实折腾了很多线程池的阻塞工作,一般不推荐,但在某些场景下可以。具体来说,控制台应用程序或 GUI 应用程序等客户端应用程序可以很好地处理它。但是,如果这是在 Web 应用程序中,那么您不希望用阻塞调用填满线程池。在那种情况下,我实际上建议使用 basic distributed architecture(link 到我的博客)将扫描拆分到一个单独的应用程序。
await the completion (e. g. via TaskCompletionSource) and process the result of each call in normal Tasks on the TheadPool
如果你想在一个单独的线程上进行并行工作,那么你可以将它包装在await Task.Run(...)
中;没有必要与 TCS 混在一起。
我想执行一个多次执行阻塞 I/O 操作的库方法(最多 67840 次调用)。该库不提供该方法的异步版本。
由于在大多数情况下调用只是等待超时,我想 运行 并行调用多个。我的方法是异步的,因此如果我能 await
结果就好了。
由于 ThreadPool
不应该用于阻塞操作,我想执行以下操作:
- 启动多个线程(例如1024)
- 运行 这些线程上的阻塞调用
await
完成(例如通过TaskCompletionSource
)并处理TheadPool
上正常任务中每次调用的结果
.NET 中是否存在 类,我可以用它来实现这样的目标?我知道 TaskCreationOptions.LongRunning
,但据我所知,这将为每次调用创建一个新线程。
blocking I/O operation... The library does not provide an async version of the method.
仅此一点,您就知道您最终不会得到“理想”的解决方案。理想情况下,I/O 是异步执行的。事实上,在 Windows 上,all I/O is 在 OS 级别异步执行,每个同步 API 调用只会阻塞当前线程,直到异步操作完成。
因此,您应该接受的第一件事是您需要稍微改变一下规则。
Since in most cases the call just waits for a timeout, I want to run multiple calls in parallel.
是的。并行是一个合适的解决方案。如果可以异步执行 I/O,那么并行性 不是 是合适的解决方案,但是由于 I/O 正在阻塞(并且您无法控制那),那么并行性就是你剩下的最佳解决方案。
My method is async, therefore it would be good if I could await the result.
这不一定跟得上。异步方法部分阻塞是可以接受的,只要明确记录即可。异步签名(即“returns a Task
”并具有 *Async
后缀)意味着方法 可能 是异步的,而不是它必须 是异步的。
就个人而言,I prefer not to do thread offloading in my logic methods, and only do it when calling them from the UI layer(link 我的博客)。
Since the ThreadPool should not be used for blocking operations
好吧,这是您可以考虑遵守的规则之一。线程池确实可以很好地处理阻塞操作,实际上这是我建议的第一个解决方案。
Start a number of threads (e.g. 1024)... Run the blocking calls on these threads
如果您抛开“我想要自己的线程”部分而只使用线程池,那么答案很简单:Parallel
或 PLINQ 会工作得很好。您可以为这两种方法设置最大并行度,并且可以在线程池上设置比正常最小线程数更大的线程数,以便根据需要更快地增加线程数。
这样确实折腾了很多线程池的阻塞工作,一般不推荐,但在某些场景下可以。具体来说,控制台应用程序或 GUI 应用程序等客户端应用程序可以很好地处理它。但是,如果这是在 Web 应用程序中,那么您不希望用阻塞调用填满线程池。在那种情况下,我实际上建议使用 basic distributed architecture(link 到我的博客)将扫描拆分到一个单独的应用程序。
await the completion (e. g. via TaskCompletionSource) and process the result of each call in normal Tasks on the TheadPool
如果你想在一个单独的线程上进行并行工作,那么你可以将它包装在await Task.Run(...)
中;没有必要与 TCS 混在一起。