何时使用TaskCreationOptions.LongRunning?

时间:2016-06-03 06:59:23

标签: c# multithreading task-parallel-library task

我很想知道这一点,但从未真正找到答案。

我知道这是任务调度程序的一个提示,任务调度程序将在其中运行,并且任务调度程序可以(或现在将?)决定为该任务实例化非线程池线程。

我不知道(并且令人惊讶地在互联网上找不到)是一个“经验法则”何时将任务指定为长时间运行。是一秒多长? 30秒?一分钟? 5分钟?它是否与应用程序使用的任务量有关系?作为程序员,我应该使用线程池中的#threads进行一些计算,我创建了多少个任务,同时长时间运行了多少个任务,并根据这个来决定是否使用长时间运行的任务?

希望在这里学到一些东西。

4 个答案:

答案 0 :(得分:20)

可以量化,当现有的tp线程不能很快完成时,线程池管理器会添加超出最佳值的额外线程。它每秒执行两次,直到SetMaxThreads()设置的最大值。其中非常高默认值。最佳的是机器可用的处理器核心数,典型值为4。由于上下文切换开销,运行比可用核心更多的线程可能是有害的。

它基于假设这样做,这些现有的线程没有取得进展,因为它们没有执行足够的代码。换句话说,它们会阻塞I / O或锁定太多。因此,这些线程不能有效地使用内核,并允许额外的线程执行,这对于提高处理器使用率和完成更多工作是完全合适的。

所以它是"长期运行"当线程需要超过半秒。请记住,这是一个很长的时间,它相当于现代桌面类机器上大约40亿处理器指令。除非你运行计算量很大的代码,比如将pi的值计算到数十亿的数字,从而实际执行那些40亿条指令,否则一个实际的线程只能在 阻塞太频繁时才需要这么长时间。这是非常常见的,像dbase查询这样的东西通常很慢并且在工作线程上执行并且消耗很少的cpu。

除此之外,您还需要验证线程池管理器所做的假设是否准确。任务需要很长时间,因为它没有有效地使用处理器。任务管理器是一种简单的方法,可以查看处理器内核在您的程序中正在执行的操作,尽管它不会告诉您他们正在执行的代码。您需要进行单元测试才能看到线程单独执行。最终且唯一完全准确的方式告诉您使用LongRunning是一个合适的选择是验证您的应用确实完成了更多的工作。

答案 1 :(得分:14)

修改汉斯'答案我最赞同。

指定LongRunning的最重要原因是获得实际保证且几乎立即执行。将不会等待线程池向您的工作项发出线程。我说"实际上"因为操作系统可以自由安排你的线程。但是你会得到一些CPU的份额,而且它通常不会花很长时间才能发生。

您可以通过指定LongRunning在队列前跳跃。如果线程池处于负载状态,则无需等待线程池每秒发出2个线程。

所以你会使用LongRunning来做必须以最有效的方式但必须以及时和稳定的方式发生的事情。例如,一些UI工作,游戏循环,进度报告,......

启动和停止线程花费大约1ms的CPU时间。这远远超过发布线程池工作项。我只是将此基准测试为每秒发布和完成的3M项目。基准是非常人为的,但数量级是正确的。

记录

LongRunning是一个提示,但它在实践中绝对有效。没有启发式将您的提示考虑在内。它被认为是正确的。

答案 2 :(得分:3)

  

何时将任务指定为长时间运行

这取决于正在做什么。如果任务包含while(true) {...}并且直到应用程序关闭,那么指定LongRunning是有意义的。如果您创建任务到排队某些操作并阻止阻止当前线程,那么您并不关心(不要指定任何内容)。

这取决于其他任务正在做什么。如果您使用或不使用LongRunning执行少量任务,则无关紧要。但是创建数千个任务可能是个问题,每个任务都要求新线程。或者相反,您可能会遇到thread starvation而未指定它。

考虑它的一个简单方法是:你更喜欢新任务在新线程中运行还是你不关心?首先 - 然后使用LongRunningOption。这个doesn't mean什么任务将在另一个线程中运行,当你必须指定它时,它只是一个很好的标准。

E.g。使用ContinueWith时,LongRunningExecuteSynchronously相反(有check以防止同时指定)。如果你有多个延续,那么也许你想要避免队列的开销并在同一个线程或相反的线程中运行特定的延续 - 你不希望其中一个延续干扰他人然后你可以专门使用LongRunning 。请参阅this article(和this)以了解ExecuteSynchronously

答案 3 :(得分:2)

长时间运行的任务是可能进入等待状态,阻止其运行的线程,或占用过多CPU时间的任务(我们将回到此状态)。

有些人可能会说这个定义过于宽泛,很多任务都会长时间运行,但考虑一下,即使等待时间很小,任务仍然没有有效地使用CPU。如果这些任务的数量增加,你会发现它们没有线性地缩放到MinWorkerThreads之后(参见ThreadPool.SetMinThreads),降级非常糟糕。

将所有I / O(文件,网络,数据库等)切换为异步的方法。

由于长时间CPU密集型计算,还有长时间运行的任务。

方法是推迟计算,例如在某些点插入await Task.Yield(),或者最好通过一个接一个地调度一个任务来明确地推迟计算,每个任务处理先前分割的数据块或处理缓冲区直到a有限的时间限制。

"太多时间"由你决定。

当您处于共享线程池的环境中时,任何时间都是太长时间,您必须选择合理的值。

E.g。在IIS下的ASP.NET中,查看每个请求对最常见请求所花费的平均时间。类似地,在使用线程池的服务中,例如,对于处理消息队列,取每条消息的平均值。

更一般地说,"太多时间"是工作项排队比他们处理更快的时间。可能会有突发的工作,因此您应该在对您来说重要的时间单位上进行平均,无论是一秒钟,一分钟,10分钟等等。当您有SLA时,您应该在某处定义此间隔。

在具有合理价值之后,您必须在实践中看到是否可以增加它或者必须减少它。更常见的情况是,如果你可以增加它,那么除非你能看到显着的性能差异,否则你会更好地而不是增加它。 "显着"意味着处理项目的数量增加超过线性,因此如果它是线性的(或线性以下,它可能发生),不要这样做。

根据我的经验,如果您通过任何这些定义执行长期任务,那么通常最好管理自己的线程或线程集。