2012-05-27 36 views
8

我想我在TPL中發現了一個嚴重的錯誤。我不確定。我花了很多時間撓撓頭,無法理解行爲。誰能幫忙?TPL中的錯誤 - TaskContinuationOptions.ExecuteSynchronously?

什麼我的情況是:

  1. 我創造,做簡單的事情的任務。沒有例外等
  2. 我註冊了ExecuteSynchronously集的延續。它必須在同一個線程上。
  3. 我在默認taskscheduler(ThreadPool)上啓動任務。開始的線程繼續並等待它。
  4. 任務開始。通行證。
  5. 延續開始於與任務相同的線程(使上一個任務完成!)並進入無限循環。
  6. 等待線程沒有任何反應。不想走得更遠。等待。我檢查了調試器,任務是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. 
+0

我也認爲這是一個錯誤。這裏是文檔頁面:http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcontinuationoptions.aspx不幸的是,它並沒有對這種情況做任何說明。 – usr

+0

您有可能擴展代碼片段,以便其他人可以複製粘貼並自行運行嗎? –

+0

@usr - 它的確說「只有非常短的延續應該同步執行。」 :)但我確實認爲這是一個錯誤。 –

回答

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延續完成之前不會返回。

運行下面的代碼片段顯示它等待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); 
} 

,這讓我覺得它可能是故意的(因此更多的是文檔的錯誤不是代碼的bug)的唯一事情是Stephen's comment in this blog post

ExecuteSynchronously是優化運行的請求 繼續任務完成前面的同一個線程 任務關閉其中我們繼續,實際上運行延續爲 前面的部分過渡到af inal狀態

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(); 

謝謝大家看看這個!併爲所有評論。

問候。

3

當您考慮task inlining時,此行爲有意義。在任務開始執行之前調用Task.Wait時,調度程序將嘗試將其內聯,即在名爲Task.Wait的同一線程上運行它。這是有道理的 - 當你可以重用線程來執行任務時,爲什麼會浪費等待任務的線程呢?

現在,當指定ExecuteSynchronously時,指示調度程序在與先前任務相同的線程上執行延續 - 這是內聯情況下的原始調用線程。

請注意,當內聯不發生時,您期望的行爲的確發生了。你所要做的就是禁止內聯,這很簡單 - 既可以指定等待的超時時間,也可以傳遞取消令牌,例如

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

最後注意內聯不能保證。在我的機器上,它沒有發生,因爲任務已經在Wait呼叫之前開始,所以我的呼叫沒有被阻止。如果您想要一個可重複的塊,請撥打Task.RunSynchronously