应该何时考虑任务"长时间运行"?

时间:2016-04-14 14:39:37

标签: c# .net task-parallel-library task threadpool

在处理任务时,经验法则似乎是线程池 - 通常由例如调用Task.Run()Parallel.Invoke() - 应该用于相对较短的操作。在处理长时间运行的操作时,我们应该使用TaskCreationOptions.LongRunning标志,以便 - 据我所知 - 避免堵塞线程池队列,即将工作推送到新创建的线程。

长期运行操作到底是什么? 在时间方面有多长?在决定是否使用LongRunning时,除了预期的任务持续时间之外还有其他因素,例如预期的CPU架构(频率) ,核心数量,......)或者从程序员的角度尝试一次性运行的任务数量?

例如,假设我有500个任务要在专用应用程序中处理,每个任务需要10-20秒才能完成。我应该只使用Task.Run启动所有500个任务(例如在循环中),然后等待它们全部,可能是LongRunning,同时保留默认的最大并发级别?然后,如果我在这种情况下设置LongRunning,则不会创建500个新线程,实际上会导致大量开销和更高的内存使用量(由于额外的线程被分配)与省略{{{ 1}?这假设在等待这500个任务时,不会安排任何新任务执行。

我猜测设置LongRunning的决定取决于在给定时间间隔内对线程池发出的请求数,而LongRunning只应用于预期的任务花费大部分线程池放置任务的时间要长得多 - 根据定义,最多只占所有任务的一小部分。换句话说,这似乎是一个排队和线程池利用率优化问题,应该通过测试逐个解决,如果有的话。我是对的吗?

4 个答案:

答案 0 :(得分:8)

有点无关紧要。问题不在于时间,而在于你的代码在做什么。如果您正在进行异步I / O,那么您仅在单个请求之间的短时间内使用该线程。如果你在做CPU工作......好吧,你正在使用CPU。没有“线程池饥饿”,因为CPU已被充分利用。

真正的问题是当你正在进行使用CPU的阻塞工作时。在这种情况下,线程池饥饿导致CPU利用不足 - 你说“我需要CPU用于我的工作”然后你实际上并没有使用它。

如果您没有使用屏蔽API,则Task.RunLongRunning一起使用是没有意义的。如果必须异步运行一些遗留阻止代码,使用LongRunning可能是个好主意。总工作时间不如“你经常这样做”的重要性。如果根据用户点击GUI启动一个线程,与首先单击按钮时已包含的所有延迟相比,成本很小,您可以使用LongRunning就好了避免线程池。如果你正在运行一个产生大量阻塞任务的循环......那就停止这样做了。这是一个坏主意:D

例如,假设没有异步API替代File.Exists。因此,如果您发现这会给您带来麻烦(例如,通过错误的网络连接),您可以使用Task.Run启动它 - 并且由于您没有进行CPU工作,因此您使用{{1} }。

相比之下,如果你需要做一些基本上100%CPU工作的图像处理,那么操作需要多长时间并不重要 - 它不是LongRunning

最后,使用LongRunning的最常见情况是,当你的“工作”实际上是老派“循环并定期检查是否应该完成某些事情,然后再进行循环”。长时间运行,但99%的时间只是阻止一些等待句柄或类似的东西。同样,这仅在处理不受CPU限制但没有适当异步API的代码时才有用。如果您需要编写自己的LongRunning,可能会发现类似的内容。

现在,我们如何将此应用于您的示例?好吧,我们不能,不是没有更多的信息。如果你的代码是CPU限制的,SynchronizationContext和朋友就是你想要的 - 那些确保你只使用足够的线程来保证CPU的安全,并且可以使用线程池。如果它 CPU绑定...如果你想并行运行任务,除了使用Parallel.For之外你没有任何选择。理想情况下,这样的工作将由您可以安全地调用的异步调用和来自您自己的线程的LongRunning组成。

答案 1 :(得分:3)

  

在处理任务时,经验法则似乎是线程池 - 通常由例如调用Task.Run()或Parallel.Invoke() - 应该用于相对较短的操作。在处理长时间运行的操作时,我们应该将TaskCreationOptions.LongRunning设置为true,以便 - 据我所知 - 避免堵塞线程池队列,即将工作推送到新创建的线程。

绝大多数情况下,您根本不需要使用LongRunning,因为线程池会调整为"丢失" 2秒后进入长时间运行的线程。

LongRunning的主要问题是它会强制您使用very dangerous StartNew API

  

换句话说,这似乎是一个排队和线程池利用率优化问题,应该通过测试逐个解决,如果有的话。我是对的吗?

是。首次编写代码时,不应设置LongRunning。如果您看到由于线程池注入率导致的延迟,那么您可以小心地添加LongRunning

答案 2 :(得分:2)

您不应在案件中使用TaskCreationOptions.LongRunning。我会使用Parallel.For

如果您要创建大量任务,则不会使用LongRunning选项,就像您的情况一样。它将用于创建将要运行很长时间的几个任务。

顺便说一句,我从未在任何类似场景中使用此选项。

答案 3 :(得分:2)

正如您所指出的那样,TaskCreationOptions.LongRunning的目的是

  

允许ThreadPool继续处理工作项,即使一个任务正在运行很长一段时间

至于何时使用它:

  

它本身并不是特定的长度......如果您通过性能测试发现不使用它会导致其他工作的处理时间过长,那么您通常只会使用LongRunning。

Source