2016年8月24日水曜日

[C#]Async/Awaitの使い方

.NET Framework 4.5から実装されてライブラリの機能に組み込まれた async/await の使い方について書いていきたいと思います。

実装しているときに、今まで見てこなかったConnectAsync()、 PostAsync()、、~~Async()メソッド、最近ちょくちょく出てきているかと思います。

非同期処理なんだなという事は分かっても、いざ使うと同期処理と同じように使うと失敗してしまう、そんな存在です。


同期処理と非同期処理

 C#のTaskを絡めて説明すると、

  同期処理 ・・・関数の始めから最後まで実行して結果を返す処理
  非同期処理・・・関数呼ばれた段階で実行オブジェクト(Task)を返す処理
 
という感じになります、
実装例をかくと

同期処理
void Foo() {
 var result = Bar();
}

非同期処理
async Task Bar() {
 var result = await BarAsync();
}

このような差が出てきます。
また、awaitを使った場合、関数名の隣に async と関数名にAsyncを付けるルールがあります。

個人的には非同期処理を使うとき開発環境がawait付いてないぞ!ってエラーしてくれるので関数名にAsyncはいらないんじゃないかなって思っていますが、右へ倣えではないですが合わせて付けておく事が良いです。

ここまでは他サイトで詳しい説明が書かれています。

この通り使えばできそうに思えるのですが、UI画面操作が加わってくるとそうも言えない状況であるのが現状です。

なので、ここから具体的な使い方について触れていきます。


async/awaitのハマりどころ


まず理解しておかないといけないのがスレッド。
アプリケーションにはMain処理があってそこからプログラムが始まります。
その始まった時にいるスレッドをメインスレッドと命名しておきます。
このメインスレッドはUIを直接操作してもエラーになりません。

その他にThreadやTaskで実行するスレッドがあります。
このスレッドはUIを直接操作するとエラーになります。

スレッドでよく痛い目に遭うのがこのスレッド間の違いで、
始まりはメインスレッド、プログラムのバックグラウンドで別スレッドを動かし
変化を画面に通知するのでInvokeが必要になる、、、という部分で結構トラブルが発生します。

Threadだと処理内部で指示された側(関数内)で明示的にInvokeしてあげないといけなかった部分が
Taskになると、関数の呼び出し側で明示的にawaitするので解決できるようになります。

しかしここにハマりポイントがあって、

 タイマースレッドなどの別スレッド上でUIが更新できるか?

という問題があります。
もちろん、この答えは更新できません。

メインスレッド上でawaitするのは問題ないです。


まとめると、
 ・可能な限り、メインスレッド上でawaitするようにする
 ・バックグラウンドにはメインスレッドで動くDispatchTimerを使ってawaitする


Taskの始まりは必ずメインスレッドになるように実装すると間違いないです。

コメントを投稿

Androider