2013-08-30 27 views
12

Delay(0) her zaman doğrulanmış mı? Benim deneyimime göre:Task.Yield() versus 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(); 
     } 
    } 
} 

Bu bir konsol uygulamasıdır, bu nedenle iş parçacığı havuzu devam etmek için kullanılır. çıkışı: Task.Delay İçinde

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

cevap

21

, bu gibi görünüyor (tek parametreli (int) versiyonu hemen altında sürümünü çağırır):

[__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; 
} 

umarım görebileceğiniz gibi:

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

Bu, her zaman tamamlanmış bir görevi döndürdüğü anlamına gelir ve bu nedenle kodunuz her zaman bu await hattını geçmeye devam edecektir.

8

Evet, öyle. (Diğer mantık arasında) reflektör gösterilerde IL maddesinin kontrolünde:

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

Yani evet, geri bu durumda halihazırda tamamlanmış bir görevi size devredecek.

await uygulamasının, zaten tamamlanmış bir görevin ek bir içerik anahtarına neden olmadığından emin olmak için yapılan denetimleri içerdiğini unutmayın; bu nedenle, evet: kodunuz, burada nefesinizi duraklatmadan çalışmaya devam edecektir.

Zaten önceden tamamlanmış bir görev, yanıt zaten bilinen/kullanılabilir eşzamanlı olduğunda önerilen bir numaradır; Genel sonuç değerleri için Task s'yi önbelleğe almak da yaygındır.

+0

Harika, teşekkürler! Genel olmayan [görev tamamlanmış durumda] oluşturmak için kullanışlı bir yol gibi görünüyor (http://stackoverflow.com/a/18527377/1768303). Damien'ın cevabını teknik olarak kabul ederek ilk oldu:] – Noseratio

+0

@Noseratio Görev bittiği gibi bir şey kullanmayı düşünüyorum = Task.FromResult (true); 'çalışmak daha iyi olduğu için daha iyidir. Bence Task.Delay (0) 'tamamlanmış bir' Task' iadesi yapmak zorunda değil. – svick

+0

@svick, ben katılıyorum 'Task.FromResult (true)' daha uygun, ama ben hala '' milisaniyeDelay' değiştirerek hem eşitleme hem de uyumsuzluğu simüle edebileceğiniz gibi Task.Delay (milisaniyeDelay: 0) 'ı seviyorum. Bu davranışı değiştirebileceklerini düşünüyor musunuz? Bu, yukarıdaki kodu vererek, benim için bir kırılma değişikliği gibi görünecek. – Noseratio