2013-08-30 71 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(); 
     } 
    } 
} 

これはコンソールアプリケーションなので、スレッドプールは継続に使用されます。出力:Task.Delayインサイド

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

答えて

21

、それはこのようになりますが(単一のパラメータ(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)'は完了した 'Task'を返す必要はないと思います。 – svick

+0

@svick私は 'Task.FromResult(true)'がより適切だと思っていますが、 'millisecondsDelay'を変更するだけで、同期と非同期の継続を簡単にシミュレートできるので、' Task.Delay(millisecondsDelay:0) 'が好きです。彼らはこの行動を変えるかもしれないと思いますか?それは私にとって大きな変化のように見えて、上記のコードを与えます。 – Noseratio

関連する問題