什么时候应该考虑一项任务 "long running"?
When should a task be considered "long running"?
在处理任务时,一条经验法则似乎是线程池——通常由例如调用 Task.Run()
或 Parallel.Invoke()
- 应该用于 相对较短的 操作。当使用长 运行ning 操作时,我们应该使用 TaskCreationOptions.LongRunning
标志,以便 - 据我所知 - 避免阻塞线程池队列,即将工作推送到新的 -创建线程。
但是 long 运行ning 操作到底是什么? 在时间方面有多长? 在决定是否使用 LongRunning
时,除了预期的任务持续时间之外,是否还有其他因素需要考虑,例如预期的 CPU 体系结构(频率,核心数,...)或从程序员的角度来看一次将尝试 运行 的任务数?
例如,假设我有 500 个任务要在专用应用程序中处理,每个任务需要 10-20 秒才能完成。我是否应该使用 Task.Run(例如在一个循环中)开始所有 500 个任务,然后等待所有任务,也许是 LongRunning
,同时保留默认的最大并发级别?再一次,如果我在这种情况下设置 LongRunning
,与省略 [=14= 相比,这不会创建 500 个新线程并且实际上会导致大量开销和更高的内存使用量(由于分配了额外的线程) ]?这是假设在等待这 500 个任务时不会安排执行新任务。
我猜测设置 LongRunning
的决定取决于在给定时间间隔内对线程池发出的请求数,并且 LongRunning
应该只用于任务预计比大多数线程池放置的任务花费的时间要长得多——根据定义,最多占所有任务的一小部分。换句话说,这似乎是一个队列和线程池利用优化问题,如果有的话,应该可以通过测试逐个解决。我说得对吗?
你不应该在你的情况下使用 TaskCreationOptions.LongRunning
。我会使用 Parallel.For.
如果您要创建很多任务,就不要使用 LongRunning
选项,就像您的情况一样。它将用于创建几个将 运行 很长时间的任务。
顺便说一下,我从来没有在任何类似的情况下使用过这个选项。
有点无所谓。问题不在于时间,而在于你的代码在做什么。如果您正在执行异步 I/O,您只是在各个请求之间的短时间内使用线程。如果您正在做 CPU 工作……好吧,您正在使用 CPU。没有 "thread-pool starvation",因为 CPU 已被充分利用。
真正的问题是,当您进行阻塞工作时,不会 使用 CPU。在这种情况下,线程池饥饿会导致 CPU-未充分利用 - 你说 "I need the CPU for my work" 然后你实际上并没有使用它。
如果您不使用阻塞 API,则将 Task.Run
与 LongRunning
一起使用是没有意义的。如果您必须 运行 一些遗留的异步阻塞代码,使用 LongRunning
可能是个好主意。总工作时间不如 "how often you are doing this" 重要。如果你根据用户点击 GUI 来启动一个线程,那么与首先点击按钮的行为中已经包含的所有延迟相比,成本是微不足道的,你可以使用 LongRunning
来很好地避免线程池。如果您正在 运行ning 一个产生大量阻塞任务的循环...停止这样做。这是个坏主意 :D
例如,假设没有异步 API 替代方案 File.Exists
。因此,如果您发现这会给您带来麻烦(例如,通过错误的网络连接),您可以使用 Task.Run
启动它 - 因为您没有做 CPU 工作,所以您可以使用LongRunning
.
相比之下,如果您需要进行一些基本上 100% CPU 工作的图像处理,那么操作需要多长时间并不重要 - 这不是 LongRunning
的事情。
最后,使用 LongRunning
的最常见情况是您的 "work" 实际上是老派 "loop and periodically check if something should be done, do it and then loop again"。很长 运行ning,但 99% 的时间只是阻塞在某个等待句柄或类似的东西上。同样,这仅在处理非 CPU 绑定但没有适当的异步 API 的代码时才有用。例如,如果您需要编写自己的 SynchronizationContext
,您可能会找到类似的东西。
现在,我们如何将其应用到您的示例中?好吧,我们不能,不能没有更多信息。如果你的代码是 CPU-bound,Parallel.For
和朋友就是你想要的 - 这些确保你只使用足够的线程来饱和 CPUs,并且可以使用线程池为了那个原因。如果它是 not CPU 绑定...如果你想 运行 并行任务,除了使用 LongRunning
你真的没有任何选择.理想情况下,此类工作将包括您可以安全调用的异步调用和来自您自己的线程的 await Task.WhenAll(...)
。
正如您所指出的,TaskCreationOptions.LongRunning
的目的是
to allow the ThreadPool to continue to process work items even though one task is running for an extended period of time
至于何时使用:
It's not a specific length per se...You'd typically only use LongRunning if you found through performance testing that not using it was causing long delays in the processing of other work.
When working with tasks, a rule of thumb appears to be that the thread pool - typically used by e.g. invoking Task.Run(), or Parallel.Invoke() - should be used for relatively short operations. When working with long running operations, we are supposed to set the TaskCreationOptions.LongRunning to true in order to - as far as I understand it - avoid clogging the thread pool queue, i.e. to push work to a newly-created thread.
绝大多数时候,你根本不需要使用LongRunning
,因为线程池会调整到"losing"一个线程到一个long-运行 2秒后操作。
LongRunning
的主要问题是它迫使您使用 very dangerous StartNew
API。
In other words, this appears to be a queuing and thread pool utilization optimization problem that should likely be solved case-by-case through testing, if at all. Am I correct?
是的。首次编写代码时,切勿设置 LongRunning
。如果您看到由于线程池注入率导致的延迟,那么您可以 小心 添加 LongRunning
.
在处理任务时,一条经验法则似乎是线程池——通常由例如调用 Task.Run()
或 Parallel.Invoke()
- 应该用于 相对较短的 操作。当使用长 运行ning 操作时,我们应该使用 TaskCreationOptions.LongRunning
标志,以便 - 据我所知 - 避免阻塞线程池队列,即将工作推送到新的 -创建线程。
但是 long 运行ning 操作到底是什么? 在时间方面有多长? 在决定是否使用 LongRunning
时,除了预期的任务持续时间之外,是否还有其他因素需要考虑,例如预期的 CPU 体系结构(频率,核心数,...)或从程序员的角度来看一次将尝试 运行 的任务数?
例如,假设我有 500 个任务要在专用应用程序中处理,每个任务需要 10-20 秒才能完成。我是否应该使用 Task.Run(例如在一个循环中)开始所有 500 个任务,然后等待所有任务,也许是 LongRunning
,同时保留默认的最大并发级别?再一次,如果我在这种情况下设置 LongRunning
,与省略 [=14= 相比,这不会创建 500 个新线程并且实际上会导致大量开销和更高的内存使用量(由于分配了额外的线程) ]?这是假设在等待这 500 个任务时不会安排执行新任务。
我猜测设置 LongRunning
的决定取决于在给定时间间隔内对线程池发出的请求数,并且 LongRunning
应该只用于任务预计比大多数线程池放置的任务花费的时间要长得多——根据定义,最多占所有任务的一小部分。换句话说,这似乎是一个队列和线程池利用优化问题,如果有的话,应该可以通过测试逐个解决。我说得对吗?
你不应该在你的情况下使用 TaskCreationOptions.LongRunning
。我会使用 Parallel.For.
如果您要创建很多任务,就不要使用 LongRunning
选项,就像您的情况一样。它将用于创建几个将 运行 很长时间的任务。
顺便说一下,我从来没有在任何类似的情况下使用过这个选项。
有点无所谓。问题不在于时间,而在于你的代码在做什么。如果您正在执行异步 I/O,您只是在各个请求之间的短时间内使用线程。如果您正在做 CPU 工作……好吧,您正在使用 CPU。没有 "thread-pool starvation",因为 CPU 已被充分利用。
真正的问题是,当您进行阻塞工作时,不会 使用 CPU。在这种情况下,线程池饥饿会导致 CPU-未充分利用 - 你说 "I need the CPU for my work" 然后你实际上并没有使用它。
如果您不使用阻塞 API,则将 Task.Run
与 LongRunning
一起使用是没有意义的。如果您必须 运行 一些遗留的异步阻塞代码,使用 LongRunning
可能是个好主意。总工作时间不如 "how often you are doing this" 重要。如果你根据用户点击 GUI 来启动一个线程,那么与首先点击按钮的行为中已经包含的所有延迟相比,成本是微不足道的,你可以使用 LongRunning
来很好地避免线程池。如果您正在 运行ning 一个产生大量阻塞任务的循环...停止这样做。这是个坏主意 :D
例如,假设没有异步 API 替代方案 File.Exists
。因此,如果您发现这会给您带来麻烦(例如,通过错误的网络连接),您可以使用 Task.Run
启动它 - 因为您没有做 CPU 工作,所以您可以使用LongRunning
.
相比之下,如果您需要进行一些基本上 100% CPU 工作的图像处理,那么操作需要多长时间并不重要 - 这不是 LongRunning
的事情。
最后,使用 LongRunning
的最常见情况是您的 "work" 实际上是老派 "loop and periodically check if something should be done, do it and then loop again"。很长 运行ning,但 99% 的时间只是阻塞在某个等待句柄或类似的东西上。同样,这仅在处理非 CPU 绑定但没有适当的异步 API 的代码时才有用。例如,如果您需要编写自己的 SynchronizationContext
,您可能会找到类似的东西。
现在,我们如何将其应用到您的示例中?好吧,我们不能,不能没有更多信息。如果你的代码是 CPU-bound,Parallel.For
和朋友就是你想要的 - 这些确保你只使用足够的线程来饱和 CPUs,并且可以使用线程池为了那个原因。如果它是 not CPU 绑定...如果你想 运行 并行任务,除了使用 LongRunning
你真的没有任何选择.理想情况下,此类工作将包括您可以安全调用的异步调用和来自您自己的线程的 await Task.WhenAll(...)
。
正如您所指出的,TaskCreationOptions.LongRunning
的目的是
to allow the ThreadPool to continue to process work items even though one task is running for an extended period of time
至于何时使用:
It's not a specific length per se...You'd typically only use LongRunning if you found through performance testing that not using it was causing long delays in the processing of other work.
When working with tasks, a rule of thumb appears to be that the thread pool - typically used by e.g. invoking Task.Run(), or Parallel.Invoke() - should be used for relatively short operations. When working with long running operations, we are supposed to set the TaskCreationOptions.LongRunning to true in order to - as far as I understand it - avoid clogging the thread pool queue, i.e. to push work to a newly-created thread.
绝大多数时候,你根本不需要使用LongRunning
,因为线程池会调整到"losing"一个线程到一个long-运行 2秒后操作。
LongRunning
的主要问题是它迫使您使用 very dangerous StartNew
API。
In other words, this appears to be a queuing and thread pool utilization optimization problem that should likely be solved case-by-case through testing, if at all. Am I correct?
是的。首次编写代码时,切勿设置 LongRunning
。如果您看到由于线程池注入率导致的延迟,那么您可以 小心 添加 LongRunning
.