English 中文(简体)
TPL中的错误-TaskContinuationOptions。同步执行?
原标题:Bug in TPL - TaskContinuationOptions.ExecuteSynchronously?
  • 时间:2012-05-27 15:59:21
  •  标签:
  • .net
  • task

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

我的场景是:

  1. I create a task that does simple thing. No exceptions etc.
  2. I register a continuation with ExecuteSynchronously set. It must be on the same thread.
  3. I start the task on default taskscheduler (ThreadPool). The starting thread proceeds and waits for it.
  4. The task starts. Passes.
  5. The continuation starts on the same thread as task (making previous task completed!) and enters endless loop.
  6. Nothing happens with waiting thread. Does not want to go further. Stuck on wait. I checked in debugger, task is RunToCompletion.

这是我的代码。感谢任何帮助!

// 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. 
问题回答

代表您在connect上提交了一个错误-希望没问题:)

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

我同意这是一个bug,只是想发布一个代码片段来显示问题,而不需要无休止的等待。该错误是ExecuteSynchronous意味着在ExecuteSynchronously继续完成之前,对第一个任务的Wait调用不会返回。

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

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在这篇博客文章中的评论

ExecuteSynchronously is a request for an optimization to run the continuation task on the same thread that completed the antecedent task off of which we continued, in effect running the continuation as part of the antecedent’s transition to a final state

考虑到任务内联。当您在任务开始执行之前调用task.Wait时,调度程序将尝试内联它,即在调用task.Wait

现在,当指定了ExecuteSynchronously时,调度器会被指示在与前一个任务相同的线程上执行延续——在内联的情况下,前一个线程是原始调用线程。

请注意,当内联没有发生时,您期望的行为确实发生了。你所要做的就是禁止内联,这很容易——要么指定一个等待超时时间,要么传递一个取消令牌,例如。

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

最后请注意,内联是不保证的。在我的机器上,它没有发生,因为任务在Wait调用之前就已经开始了,所以我的WaitTask。同步运行

所以事实证明,这确实是一个bug。我想大家都同意。然而,如果这是为了表现出这样的行为,那么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();

谢谢大家看这个!以及所有评论。

当做





相关问题
Manually implementing high performance algorithms in .NET

As a learning experience I recently tried implementing Quicksort with 3 way partitioning in C#. Apart from needing to add an extra range check on the left/right variables before the recursive call, ...

Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

How do I compare two decimals to 10 decimal places?

I m using decimal type (.net), and I want to see if two numbers are equal. But I only want to be accurate to 10 decimal places. For example take these three numbers. I want them all to be equal. 0....

Exception practices when creating a SynchronizationContext?

I m creating an STA version of the SynchronizationContext for use in Windows Workflow 4.0. I m wondering what to do about exceptions when Post-ing callbacks. The SynchronizationContext can be used ...

Show running instance in single instance application

I am building an application with C#. I managed to turn this into a single instance application by checking if the same process is already running. Process[] pname = Process.GetProcessesByName("...

How to combine DataTrigger and EventTrigger?

NOTE I have asked the related question (with an accepted answer): How to combine DataTrigger and Trigger? I think I need to combine an EventTrigger and a DataTrigger to achieve what I m after: when ...