私は何かを見逃しているかもしれませんが、行うことの違いは何ですか:
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);
}
私は何かが欠けている可能性があります
あなたは。
Task.Wait
とawait task
の違いは何ですか?
レストランのウェイターにランチを注文します。注文を出してしばらくすると、友人があなたの隣に座って座って会話を始めます。 2つの選択肢があります。タスクが完了するまで友人を無視することができます。スープが届くまで待つことができ、待っている間は何もしません。または、あなたはあなたの友人に返答することができます、そして、あなたの友人が話すことをやめるとき、ウェイターはあなたにあなたのスープを持ってきます。
Task.Wait
はタスクが完了するまでブロックします。タスクが完了するまで友達を無視します。 await
はメッセージキュー内のメッセージを処理し続け、タスクが完了すると、「待機してから中断した場所を選択してください」というメッセージをキューに入れます。友達と話をすると、会話が途切れるとスープが届きます。
ここにエリックの答えを示すためのコードがあります:
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);
}
この例は、違いを非常に明確に示しています。 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を表示することで改善された例
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は複数のタスクに使用されました。
この例では、実際にはそれほどではありません。別のスレッドで返されるタスク(WCF呼び出しなど)を待機している場合、またはオペレーティングシステムへの制御を放棄している場合(ファイルIOなど)、awaitはスレッドをブロックしないため、使用するシステムリソースが少なくなります。
上記の例では、「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;
}