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

5

我有一个引擎,其中包含任意数量的轮询程序,每个程序每隔几秒钟进行一次“轮询”。我希望各个轮询程序在不同的线程中运行,但单个轮询程序内的每个“轮询”都应该是顺序执行的,以便一个在另一个之后发生。使用以下代码启动轮询过程,一切都可以正常工作:

    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个poller的测试,一切正常。如果我创建4个以上,就会遇到奇怪的行为。其中三个poller可以工作,其中一个只执行了一次轮询,剩下的则根本不会轮询。 我的任务需要长时间运行是有道理的,毕竟它们在整个程序中都在运行。但我不明白为什么没有设置此选项会出现一些不良行为。希望能得到任何帮助。
2个回答

12
当您不使用LongRunning标志时,任务将被安排在线程池线程上,而不是在其自己的(专用)线程上。这很可能是您行为改变的原因 - 当您没有使用LongRunning标志时运行时,由于进程中的其他线程,您可能会遇到线程池饥饿问题。
话虽如此,您上面的代码并没有太多意义。您通过Task....StartNew与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);
    }
}

非常有帮助!谢谢。你是怎么学习这样的东西的呢?我去掉了多余的线程创建,现在运行得很好。这很有道理,因为在我给出的代码上面,我使用了Parallel.ForEach循环来调用每个轮询器的StartPolling,所以它们已经在并发运行了。再次感谢。 - Jeremy Foster
@Jeremy:学习这个需要大量的实践和阅读;顺便说一下,我已经写了很多关于TPL的内容 - 如果你需要阅读信息,请参见:http://reedcopsey.com/series/parallelism-in-net4/ - Reed Copsey

7

TPL(任务并行库)以及传统线程池限制了线程池中的线程数量(通常是CPU内核数量的几倍,通常为2倍)。如果将一个任务标记为LongRunning,它就知道这个任务不会很快结束,可能不会对该任务施加线程限制。

如果没有使用LongRunning,它会假设您的任务会很快完成(但实际情况并非如此),因此保持在线程限制范围内。然后,如果创建的任务超过线程限制且正在运行的任务永远不会结束,TPL 将停止所有其他任务的运行,徒劳地等待那些正在运行的任务完成(而它们永远不会完成)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接