2016年8月6日土曜日

[UWP]async/awaitでデッドロック(deadlock/freeze)を回避する方法

今までデスクトップアプリを使っていた時は非同期処理は経験則で培った非同期処理を行っていたが、UWP世代になると非同期処理はasync/awaitでやれと言わんばかりにフレームワークの一部として実装されいれるため強要される。

どうやったら上手に向き合って解決していくかノウハウが溜まった所で記事にしておく。

いきなり使い方
 デッドロックをして困った場面など色々書きたい事はありますが、最初からやり方を知っていれば遭遇刷ることは無いのでいきなり使い方から説明します。

・Asyncと付くAPIは最終的にTask.Runで括って実行すること。
 →これがデッドロックの回避策。

・Task.Runは最後の呼び出し以外で使用しないこと。
 →途中で使ってもよいがパフォーマンスが落ちる。

・メソッドで async voidの時はTask.Runで括るは不要。
 →完了を待たない投機的な実行なのでデッドロックしない。

・戻り値がIAsyncActionWithProgressなどとなっているメソッドはその場でAsTaskすること。
 →画面上にくるくる回るウェイト画面を実装したりする場面ではAsTaskしなくてよいが、
  バックグラウンドな処理で一連のシーケンスを同期処理を書きたい場合に有効。

もちろん上記以外の方法でうまく付き合っていける方法があったら、また更新したいと思います。



エラー箇所の調べ方
 ここからはデッドロックにハマって悩んでしまった方用の処方箋。

呼び出すメソッドが
 async Task DosomethingAsync()
  {
    await Task.Delay(1);
  }

となっている場合、
呼び出し側の処理でOK/NGパターンを見るとこうなります。

-----
 void CallerMethod()
 {
   DosomethingAsync().Wait();  //deadlock
 }
-----
async void CallerMethod()
 {
   await DosomethingAsync();  //deadlock
 }
-----
void CallerMethod()
 {
   Task.Run(DosomethingAsync).Wait();  //OK!
 }


-----
async void CallerMethod()
 {
   await Task.Run(DosomethingAsync);  //OK!
 }


-----

ほんとに困ったときはその場でTask.Runで括ってすべて同期処理にして
最後にTask.Runしてタスク分けする方法もありと思います。

UWPで開発する上でどうしても向き合わなければ進まない問題なので、がんばりましょう。

0 件のコメント:

Androider