web-dev-qa-db-ja.com

Task.Start / WaitとAsync / Awaitの違いは何ですか?

私は何かを見逃しているかもしれませんが、行うことの違いは何ですか:

public void MyMethod()
{
  Task t = Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();
  UpdateLabelToSayItsComplete();
}

public async void MyMethod()
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  UpdateLabelToSayItsComplete();
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}
200
Jon

私は何かが欠けている可能性があります

あなたは。

Task.Waitawait taskの違いは何ですか?

レストランのウェイターにランチを注文します。注文を出してしばらくすると、友人があなたの隣に座って座って会話を始めます。 2つの選択肢があります。タスクが完了するまで友人を無視することができます。スープが届くまで待つことができ、待っている間は何もしません。または、あなたはあなたの友人に返答することができます、そして、あなたの友人が話すことをやめるとき、ウェイターはあなたにあなたのスープを持ってきます。

Task.Waitはタスクが完了するまでブロックします。タスクが完了するまで友達を無視します。 awaitはメッセージキュー内のメッセージを処理し続け、タスクが完了すると、「待機してから中断した場所を選択してください」というメッセージをキューに入れます。友達と話をすると、会話が途切れるとスープが届きます。

383
Eric Lippert

ここにエリックの答えを示すためのコードがあります:

public void ButtonClick(object sender, EventArgs e)
{
  Task t = new Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();  
  //If you press Button2 now you won't see anything in the console 
  //until this task is complete and then the label will be updated!
  UpdateLabelToSayItsComplete();
}

public async void ButtonClick(object sender, EventArgs e)
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  //If you press Button2 now you will see stuff in the console and 
  //when the long method returns it will update the label!
  UpdateLabelToSayItsComplete();
}

public void Button_2_Click(object sender, EventArgs e)
{
  Console.WriteLine("Button 2 Clicked");
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}
116
Jon

この例は、違いを非常に明確に示しています。 async/awaitを使用すると、呼び出しスレッドはブロックされず、実行を継続しません。

static void Main(string[] args)
{
    WriteOutput("Program Begin");
    // DoAsTask();
    DoAsAsync();
    WriteOutput("Program End");
    Console.ReadLine();
}

static void DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    t.Wait();
    WriteOutput("3 - Task completed with result: " + t.Result);
}

static async Task DoAsAsync()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    var result = await t;
    WriteOutput("3 - Task completed with result: " + result);
}

static int DoSomethingThatTakesTime()
{
    WriteOutput("A - Started something");
    Thread.Sleep(1000);
    WriteOutput("B - Completed something");
    return 123;
}

static void WriteOutput(string message)
{
    Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, message);
}

DoAsTaskの出力:

 [1]プログラムの開始
 [1] 1-開始
 [1] 2-タスクの開始
 [3] A-何かの開始
 [3] B-何かを完了しました
 [1] 3-結果でタスクが完了しました:123 
 [1]プログラム終了

DoAsAsync出力:

 [1]プログラムの開始
 [1] 1-開始
 [1] 2-タスクの開始
 [3] A-何かの開始
 [1]プログラム終了
 [3] B-何かを完了しました
 [3] 3-結果でタスクが完了しました:123 

更新:出力にスレッドIDを表示することで改善された例

44
Mas

Wait()、潜在的に非同期コードを同期的に実行します。待ちません。

たとえば、asp.net Webアプリケーションがあります。 UserAは/ getUser/1エンドポイントを呼び出します。 asp.netアプリプールはスレッドプール(Thread1)からスレッドを選択し、このスレッドはhttp呼び出しを行います。 Wait()を実行すると、http呼び出しが解決されるまでこのスレッドはブロックされます。待機中に、UserBが/ getUser/2を呼び出す場合、アプリプールは別のスレッド(Thread2)を提供して、http呼び出しを再度行う必要があります。 Wait()によってブロックされたThread1を使用できないため、理由なしに別のスレッドを作成しました(実際、アプリプールから実際に取得しました)。

Thread1でawaitを使用すると、SyncContextはThread1とhttp呼び出しの間の同期を管理します。単純に、http呼び出しが完了すると通知されます。一方、UserBが/ getUser/2を呼び出す場合、Thread1を再度使用してhttp呼び出しを行います。これは、一度待機するとヒットするために解放されたためです。その後、別のリクエストでさらにそれを使用できます。 http呼び出しが完了すると(user1またはuser2)、Thread1は結果を取得して呼び出し元(クライアント)に戻ることができます。 Thread1は複数のタスクに使用されました。

9
Teoman shipahi

この例では、実際にはそれほどではありません。別のスレッドで返されるタスク(WCF呼び出しなど)を待機している場合、またはオペレーティングシステムへの制御を放棄している場合(ファイルIOなど)、awaitはスレッドをブロックしないため、使用するシステムリソースが少なくなります。

8
foson

上記の例では、「TaskCreationOptions.HideScheduler」を使用して、「DoAsTask」メソッドを大幅に変更できます。メソッド自体は非同期ではありません。「DoAsAsync」では「Task」値を返し、「async」としてマークされ、いくつかの組み合わせが行われるため、「async/await」を使用するのとまったく同じです。 :

static Task DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime, TaskCreationOptions.HideScheduler); //<-- HideScheduler do the magic

    TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
    t.ContinueWith(tsk => tsc.TrySetResult(tsk.Result)); //<-- Set the result to the created Task

    WriteOutput("2 - Task started");

    tsc.Task.ContinueWith(tsk => WriteOutput("3 - Task completed with result: " + tsk.Result)); //<-- Complete the Task
    return tsc.Task;
}
3
user8545699