はじめに
研修で初めてC#に触れて見慣れぬ単語を見かけた。それは「非同期処理」である。
今まで非同期処理という言葉を使ったこともなければ聞いたこともなく、理解に時間がかかってしまった。しかし、プログラムを書く上で大切な考え方なので復習も踏まえてアウトプットしていこうと思う。
同期処理とは上から順番に処理を実行していく方法である。処理の流れは分かりやすいが、時間がかかる処理が途中に存在すると、その処理が終了するまでほかの処理が実行できなくなってしまう。そのため、プログラム全体の実行時間が長くなってしまったり、リソースの無駄が発生したりする。
非同期処理と同期処理
対して非同期処理とは処理が終わった順に結果が呼び出される方法である。時間がかかる処理が途中に存在しても、別の処理を実行することが可能であり、プログラム全体の実行時間やリソースの無駄を省くことができる。
私たちはこの非同期の考え方を無意識に生活の中で利用している。
食事を作る場面で同期処理と非同期処理で考えてみる。
「お米を炊いて、味噌汁を作り、魚を焼いて食卓に出して食べる」というフローを同期処理と非同期処理で表したものが以下の図である。
同期処理で調理を開始すると一つ一つの処理を待つ必要があるため、食事に至るまでにかなり時間がかかることが分かる。反対に非同期処理の場合、お米を炊いている「間」に魚を焼いたり、味噌汁を作ったりできるため時間の無駄を省くことができる。大抵の人は図の右側(非同期処理)の手順で調理を行うため非同期処理の考え方を利用していると言えるだろう。
C#で非同期処理を行う
図の調理フローを実際にC#で作成してみる。各調理にかかる時間(処理時間)はお米を炊く>魚を焼く>味噌汁を作る、の順に大きくなるとする。
図1のソースコードでは図2の調理タスクメソッドを非同期で呼び出す仕組みになっている。なお図2の調理タスクメソッド内のriceメソッド、fishメソッド、misosoupメソッドはCLI上に文字を表示するのみの機能のため省略した。
実行結果は図3のとおりである。
実行結果から調理プログラムが非同期処理で実行されていることが分かる。
時間のかかるお米を炊く動作を実行した後にその処理を待ってからほかの処理を行うのではなく、実行中に魚を焼く処理、味噌汁を作る処理を行い、処理が終わった順番から出力されている。
最後にご飯が炊けて、完成となる。この流れがまさに非同期処理の特徴である。
C#において非同期処理を実現するためには「Task」、「async」、「await」が必要である。
Taskは非同期操作を表すクラスであり、asyncを付けたメソッドを非同期メソッドという。また、awaitを使うことで「指定したTaskの終了を待ち、その結果を取り出す」ことができる。
今回のプログラムでは各調理メソッドを1つのTaskとして扱い、asyncで非同期メソッドとして定義した。
ほかにも非同期処理の方法としてThreadクラスを用いる方法があるが今回はTask、async、awaitを用いた方法で実行した。
まとめ
- 非同期処理は処理が終わった順番から結果が呼び出される
- C#では「Task」「async」「await」を用いて非同期処理を実現する