TAP 和 async/await 是不是意味着用于专用线程?

Posted

技术标签:

【中文标题】TAP 和 async/await 是不是意味着用于专用线程?【英文标题】:Are TAP and async/await meant to be used for dedicated threads?TAP 和 async/await 是否意味着用于专用线程? 【发布时间】:2020-07-26 04:53:01 【问题描述】:

我有一些线程专用于一项任务(接收和处理请求)和一些线程专用于其他任务(长时间运行/CPU 密集型任务)。 这是一个控制台应用程序。

我想在所有线程中使用 async/await,但我了解到 async/await 使用 managed thread pool 来运行延续,这意味着请求处理可能会因为出现一堆 CPU 密集型任务而延迟(是的,这么多,这就是为什么我有有限的线程数来做这项工作)?

    如果我想限制并发,那我一定不能使用TAP? 如果我想在应用程序的生命周期内做一些后台工作,那么我也不能使用 TAP 吗? 我应该用它自己的 SyncronizationContext 编写一个 ThradPool 实现吗? 还有其他方法可以在某些线程中运行延续吗?

【问题讨论】:

如何通过使用SetMinThreads 方法增加ThreadPool 产生的初始线程数?例如ThreadPool.SetMinThreads(100, 10) @TheodorZoulias 我不喜欢这个解决方案,因为请求处理和长时间运行的任务仍然混合在一个线程池中,这仍然可能导致没有线程来处理请求的情况,因为它们都是忙于长时间运行的任务。而且我真的不需要这么多线程来处理长时间运行的任务(现在 3 个就足够了)。 使用TaskCreationOptions.LongRunning 选项启动长时间运行的任务怎么样?顺便说一句,当您说“长时间运行”时,您是指从应用程序开始到结束,还是只持续几秒钟以上? LongRunning 选项真的没什么特别的。它只是在新线程而不是ThreadPool 线程中运行任务。我的猜测是,您长时间运行的任务实际上是被Thread.Sleep 延迟的循环。如果是这种情况,那么您可以将Thread.Sleep 替换为await Task.Delay,并且具有不需要专用线程的长时间运行的任务。 Here 就是一个例子。 在程序的整个生命周期中,这些任务可能不会在紧密循环中旋转。我猜他们大部分时间都在等待什么。他们可以通过使用SemaphoreSlin.WaitAsync 或像Channel 这样的异步队列来异步等待,而不是阻塞当前线程。老实说这不会有太大的不同。浪费 3 个线程只是 3 MB 未充分利用的 RAM,这对于现代计算机来说不算什么。 【参考方案1】:

我想在所有线程中使用 async/await,但我了解到 async/await 使用托管线程池来运行延续,这意味着请求处理可能会因为出现一堆 CPU 密集型任务而延迟(是的,这么多,这就是为什么我有有限的线程数来做这项工作)?

这是一个足够接近的分析。从技术上讲,不是async/await 需要线程池进行延续,而是内部的 TAP 方法实现。也就是说,这是 BCL 中的一种常见模式,因此,耗尽线程池会导致异步完成延迟。

如果我想限制并发,那我一定不能使用TAP?

只要不压垮线程池,就可以使用TAP。限制并发将释放线程池,从而启用 TAP。

如果我想在应用程序的生命周期中在后台做一些工作,那么我也不能使用 TAP 吗?

我认为它更像是“不要淹没工作中的线程池”。耗尽的线程池不适合任何类型的工作。

我应该用它自己的 SyncronizationContext 编写一个 ThradPool 实现吗? 还有其他方法可以在某些线程中运行延续吗?

您可以使用SynchronizationContext 来控制await 恢复执行的位置,但仍有BCL 内部完成需要排队到线程池。没有控制。唯一真正的选择是保持线程池空闲。

【讨论】:

>As long as you don't overwhelm the thread pool, you can use TAP. 那么,如果我收到 1000 个工作项,那么我应该将它们缓存在某处并在它们完成时将它们排入队列?现在我在单独的线程中使用无限循环来执行这些作业项(使用生产者-消费者模式将作业项排入该线程)。 我很困惑。如果您的作业项已经由专用线程处理,那么您首先如何遇到线程池耗尽的问题? 我想在所有线程中使用 async/await,我还没有这样做。我正在尝试在所有可能的问题发生之前对其进行分析。 如果您有专门的线程用于 CPU 密集型操作,您可以为这些专用线程使用 SynchronizationContext,从而防止它们在使用 async 时耗尽线程池。例如,AsyncContextAsyncContextThread 来自 AsyncEx。

以上是关于TAP 和 async/await 是不是意味着用于专用线程?的主要内容,如果未能解决你的问题,请参考以下文章

用 async/await 来处理异步

用 async/await 来处理异步

ES2017 Async/await 函数 - 它们是不是仅适用于 Promise?

ES6中async和await说明和用法

理解异步函数async和await的用法

ShowDialog 中的 Catel async await 命令 - 死锁