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

13 浏览
0 Comments

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

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

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
0 Comments

当不使用TaskCreationOptions.LongRunning时,会出现奇怪的行为。这个问题的原因是TPL(以及传统的线程池)限制了线程池中的线程数量(通常是CPU核心数量的一个小倍数,通常是2倍核心)。如果将一个任务标记为LongRunning,它知道该任务不会很快完成,并且可能不会将该任务受到线程限制的影响。

如果没有使用LongRunning,它会认为您的任务会很快完成(但实际上并不是这样),所以它会在线程限制内保持。然后,如果创建的任务数量超过线程限制,并且正在运行的任务永远不会结束,TPL会停止所有其他任务的运行,徒劳地等待这些正在运行的任务完成(但它们永远不会完成)。

解决这个问题的方法是将任务标记为LongRunning,这样TPL将不会将其受到线程限制的影响。这样,即使有很多任务在运行,TPL也会为它们分配足够的线程来执行任务,而不会停止其他任务的运行。

0
0 Comments

当你不使用LongRunning标志时,任务将被安排在线程池线程上,而不是它自己的(专用)线程。这很可能是行为变化的原因 - 当你没有使用LongRunning标志时运行时,可能会由于进程中的其他线程导致线程池饥饿。

尽管如此,你上面的代码并没有太多意义。你正在启动一个专用线程(通过使用带有LongRunning的Task....StartNew)来启动一个任务,然后立即调用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循环调用每个poller上的StartPolling,所以它们已经在并发运行了。再次感谢。

学习这些需要很多实践和大量阅读。顺便说一下,我写了很多关于TPL的文章 - 如果你需要阅读信息,请参考:reedcopsey.com/series/parallelism-in-net4

0