2013-08-30 22 views
12

Delay(0)是否始終處於內聯狀態?根據我的經驗,它可以:Task.Yield()與Task.Delay(0)

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApplication 
{ 
    class Program 
    { 
     static async Task Test() 
     { 
      await Task.Yield(); 
      Console.WriteLine("after Yield(), thread: {0}", Thread.CurrentThread.ManagedThreadId); 
      await Task.Delay(0); 
      Console.WriteLine("after Delay(0), thread: {0}", Thread.CurrentThread.ManagedThreadId); 
      await Task.Delay(100); 
      Console.WriteLine("after Delay(100), thread: {0}", Thread.CurrentThread.ManagedThreadId); 
     } 
     static void Main(string[] args) 
     { 
      Console.WriteLine("Main thread: {0}", Thread.CurrentThread.ManagedThreadId); 
      Test().Wait(); 
     } 
    } 
} 

這是一個控制檯應用程序,因此線程池用於延續。輸出:

Main thread: 11 
after Yield(), thread: 7 
after Delay(0), thread: 7 
after Delay(100), thread: 6 

回答

21

裏面Task.Delay,它看起來像這樣(單個參數(int)版本只是調用下面的版本):

[__DynamicallyInvokable] 
public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken) 
{ 
    if (millisecondsDelay < -1) 
    { 
     throw new ArgumentOutOfRangeException("millisecondsDelay", Environment.GetResourceString("Task_Delay_InvalidMillisecondsDelay")); 
    } 
    if (cancellationToken.IsCancellationRequested) 
    { 
     return FromCancellation(cancellationToken); 
    } 
    if (millisecondsDelay == 0) 
    { 
     return CompletedTask; 
    } 
    DelayPromise state = new DelayPromise(cancellationToken); 
    if (cancellationToken.CanBeCanceled) 
    { 
     state.Registration = cancellationToken.InternalRegisterWithoutEC(delegate (object state) { 
      ((DelayPromise) state).Complete(); 
     }, state); 
    } 
    if (millisecondsDelay != -1) 
    { 
     state.Timer = new Timer(delegate (object state) { 
      ((DelayPromise) state).Complete(); 
     }, state, millisecondsDelay, -1); 
     state.Timer.KeepRootedWhileScheduled(); 
    } 
    return state; 
} 

正如你所希望看到:

if (millisecondsDelay == 0) 
    { 
     return CompletedTask; 
    } 

這意味着它總是返回一個完成的任務,因此您的代碼將始終繼續運行,通過特定的await行。

8

是的,它的確如此。在IL中的反射器顯示的檢查(包括其他邏輯):

if (millisecondsDelay == 0) 
{ 
    return CompletedTask; 
} 

所以,是的,它會交給你回來在這種情況下,一個已經完成的任務。

請注意,await的執行包括檢查,以確保已完成的任務不會導致額外的上下文切換,所以是的:您的代碼將保持運行而不會在此暫停。

回答已完成的任務是一個推薦的技巧,當答案已知/可同步;緩存Task以獲取常見結果值也很常見。

+0

太好了,謝謝!在我看來,像一個方便的方式來創建一個非通用的[任務完成狀態](http://stackoverflow.com/a/18527377/1768303)。接受達米安的答案在技術上他是第一個:] – Noseratio

+0

@Noseratio我認爲使用類似「Task completed = Task.FromResult(true);'更好,因爲它可以保證工作。我認爲'Task.Delay(0)'不需要返回完成的'任務'。 – svick

+0

@svick,我同意'Task.FromResult(true)'更合適,但我仍然喜歡'Task.Delay(millisecondsDelay:0)',因爲我可以通過改變'millisecondsDelay'輕鬆地模擬同步和異步延續。你認爲他們可能會改變這種行爲?對我來說,這看起來像是一個突破性的變化,給出了上面的代碼。 – Noseratio