avs vs Task.Wait-Deadlock?


194

私はかなりの違いを理解していないTask.Waitとしますawait

ASP.NET WebAPIサービスには、次のような機能があります。

public class TestController : ApiController
{
    public static async Task<string> Foo()
    {
        await Task.Delay(1).ConfigureAwait(false);
        return "";
    }

    public async static Task<string> Bar()
    {
        return await Foo();
    }

    public async static Task<string> Ros()
    {
        return await Bar();
    }

    // GET api/test
    public IEnumerable<string> Get()
    {
        Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());

        return new string[] { "value1", "value2" }; // This will never execute
    }
}

どこでGetデッドロックします。

何が原因でしょうか?ブロッキング待機ではなく、これを使用すると問題が発生しないのはなぜawait Task.Delayですか?


@Servy:時間があるとすぐにレポで戻ります。今のところ、Task.Delay(1).Wait()これで十分です。
ronag 2012年

2
Task.Delay(1).Wait()基本的にはとまったく同じですThread.Sleep(1000)。実際の製品コードではめったに適切ではありません。
2012年

@ronag:あなたWaitAllがデッドロックを引き起こしています。詳細については、回答のブログへのリンクを参照してください。await Task.WhenAll代わりに使用する必要があります。
スティーブンクリアリー

6
@ronagあなたは持っているので、単一の呼び出しをかデッドロックはありませんが、あなたは以上のものを作成し、それらのすべてを待っている列挙を持っているので、最初のバーは、第二のデッドロックします。ASPコンテキストをブロックしないように、すべてのタスクを待機する代わりに、メソッドが正常に戻ることがわかります。ConfigureAwait(false)BarRosawait Task.WhenAll
12

2
@ronagあなたの他のオプションを追加することです.ConfigureAwait(false) ツリーまでのすべての道を通行何もされていることを、あなたはブロックされるまで、これまで戻って、メインのコンテキストに取得しようとしていません。それはうまくいくでしょう。別のオプションは、内部同期コンテキストをスピンアップすることです。 リンク。あなたがに入れればTask.WhenAllAsyncPump.RunそれはあなたがConfigureAwaitどこにも必要とせずに事実上全体をブロックするでしょうが、それはおそらく過度に複雑な解決策です。
12

回答:


268

Waitそしてawait-概念的に類似しながら、 -実際には完全に異なっています。

Waitタスクが完了するまで同期的にブロックします。そのため、現在のスレッドは、タスクが完了するのを待って文字通りブロックされます。原則として、「asyncずっと下」を使用する必要があります。つまり、asyncコードをブロックしないでください。私のブログでは、非同期コードでのブロッキングどのようにデッドロックを引き起こすかについて詳しく説明しています

awaitタスクが完了するまで非同期に待機します。これは、現在のメソッドが「一時停止」され(状態がキャプチャされ)、メソッドが呼び出し元に不完全なタスクを返すことを意味します。その後、await式が完了すると、メソッドの残りの部分が継続としてスケジュールされます。

また、「協調ブロック」についても触れました。これは、実行中のタスクがWait待機中のスレッドで実行される可能性があることを意味します。これが発生する可能性がある状況もありますが、これは最適化です。タスクが別のスケジューラー用である場合、タスクが既に開始されている場合、またはコード以外のタスクである場合(コード例など)、コードがないためタスクをインラインで実行できないなど、発生しない状況が数多くあります。それのための)。WaitDelay

あなたは私のasync/ awaitイントロが役に立つかもしれません。


4
私は誤解があると思いますが、Waitうまくawaitデッドロックします。
ronag 2012年

5
いいえ、タスクスケジューラはそれを行いません。Waitスレッドをブロックし、他のものには使用できません。
スティーブンクリアリー2012年

8
@ronag私の推測では、メソッド名が混同されているだけで、デッドロックは実際にはブロッキングコードによって引き起こされ、コードで機能していましたawait。それか、デッドロックがどちらとも無関係であり、問​​題を誤って診断した。
2012年

3
@hexterminator:これは仕様によるものです。UIアプリではうまく機能しますが、ASP.NETアプリでは邪魔になる傾向があります。ASP.NET Coreはを削除することでこれを修正したSynchronizationContextため、ASP.NET Coreリクエスト内でブロックしてもデッドロックは発生しなくなりました。
Stephen Cleary

1
ここのmsdnでは、待機は別のスレッドで非同期に実行されると記載されています。何か不足していますか?msdn.microsoft.com/en-us/library/hh195051(v=vs.110).aspx
batmaci 2017

6

私がさまざまなソースから読んだものに基づいて:

await式は、それが実行されているスレッドをブロックしません。その代わりに、コンパイラーasyncは、待機しているタスクの継続として、メソッドの残りの部分にサインアップします。次に、制御はasyncメソッドの呼び出し元に戻ります。タスクが完了すると、タスクは継続を呼び出し、asyncメソッドの実行は中断したところから再開します。

シングルtaskが完了するのを待つには、そのTask.Waitメソッドを呼び出します。呼び出しWait、単一クラスのインスタンスまでのメソッドブロック呼び出しスレッドが実行を完了しました。パラメーターなしWait()メソッドは、タスクが完了するまで無条件に待機するために使用されます。タスクはThread.Sleep、2秒間スリープするメソッドを呼び出すことにより、作業をシミュレートします。

この記事も良い読み物です。


3
「それでは技術的に不正確ではないですか?誰かが明確にしていただけませんか?」-明確にできますか。あなたはそれを質問として尋ねていますか?(私はあなたが尋ねているのか答えているのかを明確にしたいだけです)。あなたが質問している場合:別の質問としてはうまくいくかもしれません。ここで回答として新しい応答を収集することはほとんどありません
Marc Gravell

1
私は質問に答え、ここで私が持っていた疑いについて別の質問をしましたstackoverflow.com/questions/53654006/…ありがとう@MarcGravell。今すぐ回答の削除投票を削除していただけますか?
アユシュマティ

「今すぐ回答の削除投票を削除していただけますか?」-それは私のものではありません。♦のおかげで、私によるそのような投票はすぐに有効になります。しかし、これが質問の要点、つまりデッドロックの振る舞いに関する答えになるとは思いません。
Marc Gravell

-2

いくつかの重要な事実は他の回答では与えられていません:

「非同期待機」はCILレベルではより複雑であるため、メモリとCPU時間を消費します。

待機時間が許容できない場合は、タスクをキャンセルできます。

「非同期待機」の場合、そのタスクをキャンセルまたは監視するためのハンドラーはありません。

Taskの使用は、「非同期待機」よりも柔軟です。

同期機能は、非同期でラップできます。

public async Task<ActionResult> DoAsync(long id) 
{ 
    return await Task.Run(() => { return DoSync(id); } ); 
} 

同期と非同期の方法でコードを複製したり、ハックを使用したりする必要がある理由がわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.