TPL中的错误 - TaskContinuationOptions.ExecuteSynchronously?

时间:2012-05-27 15:59:22

标签: .net task-parallel-library task

我想我在TPL中发现了一个严重的错误。我不确定。我花了很多时间挠挠脑袋,无法理解这种行为。有人可以帮忙吗?

我的情景是:

  1. 我创建了一个简单的任务。没有例外等。
  2. 我使用ExecuteSynchronously set注册了一个延续。它必须在同一个线程上。
  3. 我在默认的taskcheduler(ThreadPool)上启动任务。起始线程继续进行并等待它。
  4. 任务开始。通行证。
  5. 继续在与任务相同的线程上开始(使前一个任务完成!)并进入无限循环。
  6. 等待线程没有任何反应。不想再往前走。坚持等待。我在调试器中检查过,任务是RunToCompletion。
  7. 这是我的代码。感谢任何帮助!

    // note: using new Task() and then Start() to avoid race condition dangerous
    // with TaskContinuationOptions.ExecuteSynchronously flag set on continuation.
    
    var task = new Task(() => { /* just return */ });
    task.ContinueWith(
       _task => { while (true) { } /* never return */ },
       TaskContinuationOptions.ExecuteSynchronously);
    
    task.Start(TaskScheduler.Default);
    task.Wait(); // a thread hangs here forever even when EnterEndlessLoop is already called. 
    

3 个答案:

答案 0 :(得分:5)

代表您提交连接错误 - 希望没问题:)

https://connect.microsoft.com/VisualStudio/feedback/details/744326/tpl-wait-call-on-task-doesnt-return-until-all-continuations-scheduled-with-executesynchronously-also-complete

我同意这是一个错误,只是想发布一个代码片段,显示问题而不会有“无休止”的等待。错误是ExecuteSynchronously意味着第一个任务上的Wait调用不会返回,直到ExecuteSynchronously continuation也完成。

运行以下代码段显示它等待17秒(因此所有3个必须完成而不是仅完成第一个)。无论第三个任务是在第一个任务还是第二个任务计划之后都是一样的(因此,这个ExecuteSynchronously将继续通过这样安排的任务树)。

void Main()
{
    var task = new Task(() => Thread.Sleep(2 * 1000));
    var secondTask = task.ContinueWith(
        _ => Thread.Sleep(5 * 1000),
        TaskContinuationOptions.ExecuteSynchronously);
    var thirdTask = secondTask.ContinueWith(
        _ => Thread.Sleep(10 * 1000),
        TaskContinuationOptions.ExecuteSynchronously);

    var stopwatch = Stopwatch.StartNew();
    task.Start(TaskScheduler.Default);
    task.Wait();
    Console.WriteLine ("Wait returned after {0} seconds", stopwatch.ElapsedMilliseconds / 1000.0);
}

唯一让我认为可能是故意的(因此更多的是文档错误而不是代码错误)是Stephen's comment in this blog post

  

ExecuteSynchronously是一个优化运行请求   完成前提的同一线程上的延续任务   我们继续执行任务, 实际上继续运行   前因过渡到最终状态的一部分

答案 1 :(得分:3)

当您考虑task inlining时,这种行为很有意义。在任务开始执行之前调用Task.Wait时,调度程序将尝试内联它,即在调用Task.Wait的同一线程上运行它。这是有道理的 - 为什么在重用线程以执行任务时浪费线程等待任务?

现在,当指定ExecuteSynchronously时,调度程序被指示在与先行任务相同的线程上执行continuation - 在内联的情况下是原始调用线程。

请注意,当内联没有发生时,您所期望的行为确实会发生。您所要做的就是禁止内联,这很简单 - 指定等待超时或传递取消令牌,例如。

task.Wait(new CancellationTokenSource().Token); //This won't wait for the continuation

最后请注意,内联并不能保证。在我的机器上,它没有发生,因为任务已经在Wait通话之前开始,所以我的Wait通话没有阻止。如果您想要一个可重现的块,请致电Task.RunSynchronously

答案 2 :(得分:0)

事实证明这确实是一个错误。我想每个人都同意。但是,如果打算这样做,那么API和文档似乎具有很大的误导性。

我使用的工作只是使用ManualResetEventSlim。

var eventSlim = new ManualResetEventSlim(false);
var task = new Task(() => { eventSlim.Set(); });
task.ContinueWith(
   _task => { while (true) { } /* never return */ },
   TaskContinuationOptions.ExecuteSynchronously);

task.Start(TaskScheduler.Default);
eventSlim.Wait();

谢谢大家看看这个!对于所有评论。

问候。