我有一个具有任意数量轮询器的引擎,每个轮询器每隔几秒进行一次“轮询”。我希望轮询器在不同的线程中运行,但是单个轮询器中的每个“轮询”应该是顺序的,以便在下一个轮询器之后发生。一切正在使用此代码启动轮询过程:
public void StartPolling()
{
Stopwatch watch = new Stopwatch();
while (Engine.IsRunning)
{
Task task = Task.Factory.StartNew(() =>{
watch.Restart();
Poll();
watch.Stop();
},TaskCreationOptions.LongRunning);
task.Wait();
if(Frequency > watch.Elapsed) Thread.Sleep(Frequency - watch.Elapsed);
}
}
然而,我花了一段时间才发现TaskCreationOptions.LongRunning选项,它解决了我遇到的一个我仍然不明白的奇怪问题。 没有这个选项,如果我运行一个创建1-3个这些轮询器的测试,一切都运行正常。如果我创造了4+然后我遇到了奇怪的行为。三个轮询器可以工作,一个只执行一个轮询,剩下的任何轮询都不会轮询。 完全可以理解我的任务是长期运行的。毕竟他们正在运行我的整个程序。但我不明白为什么没有这个选项设置我会得到一些不好的行为。任何帮助将不胜感激。
答案 0 :(得分:12)
当您不使用LongRunning
标志时,任务将在线程池线程上调度,而不是在其自己的(专用)线程上。这可能是您行为改变的原因 - 当您在没有LongRunning
标志的情况下运行时,由于您的进程中的其他线程,您可能会遇到线程池饥饿。
话虽如此,您的上述代码并没有多大意义。你正在启动一个专用线程(通过Task ... StartNew with LongRunning)来启动任务,然后立即调用阻止当前线程的task.Wait()
。最好在当前线程中按顺序执行此操作:
public void StartPolling()
{
Stopwatch watch = new Stopwatch();
while (Engine.IsRunning)
{
watch.Restart();
Poll();
watch.Stop();
if(Frequency > watch.Elapsed) Thread.Sleep(Frequency - watch.Elapsed);
}
}
答案 1 :(得分:7)
TPL(和传统的ThreadPool)限制池中的线程数(通常是CPU核心数量的一小部分,通常是2x核心)。如果您将任务标记为LongRunning
,则它知道该任务不会很快完成,并且可能不会将此任务置于线程限制之下。
如果没有LongRunning
,它会假定您的任务将快速完成(它没有完成),因此它保持在线程限制范围内。然后,如果您创建的任务多于线程限制并且正在运行的任务永远不会结束,则TPL会阻止所有其他任务运行等待那些正在运行的任务完成(这是他们永远不会做的)。