当我不使用TaskCreationOptions.LongRunning时出现奇怪的行为

时间:2011-03-14 05:58:46

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

我有一个具有任意数量轮询器的引擎,每个轮询器每隔几秒进行一次“轮询”。我希望轮询器在不同的线程中运行,但是单个轮询器中的每个“轮询”应该是顺序的,以便在下一个轮询器之后发生。一切正在使用此代码启动轮询过程:

    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+然后我遇到了奇怪的行为。三个轮询器可以工作,一个只执行一个轮询,剩下的任何轮询都不会轮询。 完全可以理解我的任务是长期运行的。毕竟他们正在运行我的整个程序。但我不明白为什么没有这个选项设置我会得到一些不好的行为。任何帮助将不胜感激。

2 个答案:

答案 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会阻止所有其他任务运行等待那些正在运行的任务完成(这是他们永远不会做的)。