2016-04-15 3 views
7

私はConfigureAwait(false)で非同期ライブラリメソッドを呼び出しています。しかし、私はまだデッドロックで終わる。 (ASP.NETコントローラAPIで使用しています) しかし、同じメソッドをTask.Run()にラップすると、正常に動作します。Task.Run()の動作中にConfigureAwait(false)が機能しないのはなぜですか?

ライブラリメソッドがConfigureAwaitを内部的に使用していない場合、ConfigureAwaitを追加してもライブラリ呼び出しで問題が解決されない場合、デッドロックが発生します(.Resultを使用してブロックします)。しかし、その場合、同じコンテキスト/スレッドで続行できないため、Task.Run()で動作するのはなぜですか。

このarticleはこれについて語ります。 Btw、私はStephen Clearyの記事をたくさん用意しています。しかし、なぜTask.Run()が動作するのは謎です。

コードスニペット:現在SynchronizationContextを使用して継続を実行しようとしているので、最初の例で

// This Create Method results in Deadlock 
public async Task<string> Create(MyConfig config) 
{ 
     Document doc = await Client.CreateDocumentAsync(CollectionUri, config).ConfigureAwait(false); 
     return doc.Id; 
} 

// Uses Task.Run() which works properly, why?? 
public string Create(MyConfig config) 
{ 
    Document doc = Task.Run(() => Client.CreateDocumentAsync(CollectionUri, config)).Result; 
    return doc.Id; 
} 

[HttpPost] 
public ActionResult CreateConfig(MyConfig config) 
{ 
    string id = Create(config).Result; 
    return Json(id); 
} 

答えて

5

Client.CreateDocumentAsyncの実装がデッドロックされています。

Task.Runを使用すると、デリゲートはThreadPoolスレッドで呼び出されます。つまり、現在のSynchronizationContextが存在しないことを意味します。すべての継続はThreadPoolスレッドを使用して再開されます。これはデッドロックしないことを意味します。

なぜあなたのCreateConfigメソッドは非同期ではありませんか? MVCとWebAPIの最新のバージョンでは、非同期メソッドをサポートしており、.Resultを取り除くのが最良の解決策になります。

+0

ConfigureAwait(false)を使用して、ThreadPoolを使用して継続を確実にする必要はありませんか?はい、CreateConfigメソッドを非同期にしました。しかし、Task.Run()が何の問題も生じない理由を正確に理解したかったのです。 –

+4

@KrunalModi 'ConfigureAwait(false)'は、 'Client.CreateDocumentAsync'の呼び出しではなく、スレッドプールの使用を継続するよう設定します。 – Lukazoid

+0

@KrunalModi内部的に別の非同期呼び出しを持つ非同期呼び出しで' ConfigureAwait'を使用するポイントがありません。 * ConfigureAwaitで使用されていません。 'ConfigureAwait(false)'は、*ほぼ*どこでも、特にライブラリで使用するべきです。 UI同期コンテキストで継続が実行されるべきであるため、トップレベルの呼び出し(UI同期コンテキストの呼び出し)だけが 'ConfigureAwait(false)'を必要としません。 'await Task.Run(()=> ...)'または 'await Task.Run(()=> ...)を使って' ConfigureAwait(false) 'を内部的に使用しないライブラリの場合ConfigureAwait(false ) 'は良い回避策です。 –

4

私はルカゾイドが正しいと信じています。別の言い方をするには...

// This Create Method results in Deadlock 
public async Task<string> Create(MyConfig config) 
{ 
    Document doc = await Client.CreateDocumentAsync(CollectionUri, config).ConfigureAwait(false); 
    return doc.Id; 
} 

あなたはただConfigureAwait(false) 1のレベルに固執することはできません、それは魔法のようにデッドロックを防ぐいます。 ConfigureAwait(false)は、そのメソッドの推移的クロージャーおよびそれが呼び出すすべてのメソッドですべてのawaitによってデッドロックが使用される場合にのみ、デッドロックを防ぐことができます。言い換えれば

は、ConfigureAwait(false)は、(それは)Create内のすべてのawaitに使用する必要があり、それはまた、(私たちが知らない)CreateDocumentAsync内のすべてのawaitに使用する必要があり、それはまた、する必要がありますawaitごとにCreateDocumentAsyncなどが呼び出されるすべてのメソッドで使用されます。

これは、デッドロックの問題に対する脆弱な「解決策」の1つの理由です。

+0

正しい決定を下すためには、すべての非同期コールが適切に文書化されている必要があります。'await [async call] .ConfigureAwait(false)'または 'await Task.Run(async()=> async callを待つ)。ConfigureAwait(false)'。 –

+1

@ ThanasisIoannidis:理論的には素晴らしいですが、実際には 'ConfigureAwait(false)'が不足しているのは通常間違いです。したがって、ドキュメントはちょうど間違っています。 –

関連する問題