非同期(並列)とは複数の処理を同時に、並行して行うことを指します。通常、プログラミングは特定の処理を順次行っていくことが一般的ですが、それでは重い処理が連続して動くような場合では画面が止まったように感じることになってしまいます。
そんな時に非同期・並列で処理をするような書き方をすれば、パソコンの処理能力を効率よく利用することができ、待機時間を減らすことができるようになります。
非同期処理の概要
非同期処理は簡単に言ってしまうと「並行処理」の意味に当たります。ある処理を別の場所で処理するようにして、自分はさらに別の処理をするようなことを言います。たとえば日常生活で良く利用するコンビニを例に取ってみます。
コンビニでお弁当を購入すると電子レンジで温めてくれますよね。この時、店員さんが温まっているのを待っているのは時間の無駄です。しかし、お弁当を温めるのは電子に任ることで、次のお客さんの会計を進めることができます。温めが完了したら会計作業を中断し、お弁当を待っているお客さんに渡してから会計作業を再開するようなこともできますね。
かなりざっくばらんな言い方になっていますが、これが一種の並列処理である「非同期処理」に当たります。お弁当を温めるという作業を電子レンジに任せることで、自分は他の作業をすることができ、作業を効率よく進めることができます。
これに対して、順次処理(逐次処理)では、お弁当を温めが終わるまでは次のお客さんの会計を進めることなどせず、温め終わったらお客さんにお弁当を渡してから、次のお客さんの会計作業を始めるような感じです。これではお弁当を温める時間が無駄になってしまいます。
非同期プログラミングは作業を別の部分で行うようにし、作業を効率よくすることが可能になります。主にCPUの使用効率を上げることで処理速度を改善することができるのが「非同期処理」だと思ってください。
非同期処理を使う理由
非同期処理のイメージについて紹介してきました。効率性を上げられるのが非同期処理のメリットとなりますが、もう少し深く非同期処理を使う目的を考えてみます。非同期処理を使う目的は主に以下の 3 つを挙げることができます。
- UI を改善するため
- CPU ブロックを防止するため
- 処理性能を向上させるため
同期処理を使用している場合は、一つの作業を行っている間に他の作業をすることができくなりため、別の要求を受け入れることができなくなります。例えばボタンを押したことで動く処理が 10 秒掛かるとすると、ボタンを押したあと 10 秒間は UI がフリーズしたような状況になってしまいます。そうした事態を避けるために非同期処理を導入します。
また、近年のコンピューターの性能の向上により、パソコンでは複数の CPU が搭載されることが当たり前になりました。非同期処理を導入することにより、CPU を効率よく使用することができるため、処理速度を大幅に向上させることができるのです。
非同期処理の注意点
非同期を扱う上で注意するべき点は、プログラミングの「複雑性」がぐっと上がるという点です。非同期処理は作業を同時に行うため、記述された通りに行う逐次処理とは時間の捉え方が変わります。処理順番に依存するような場合やデータの整合性が取れなくなるような場合は特に注意が必要です。
数人が同時に働く調理場を思い浮かべてみてください。色々な人が作業を並行して行っています。ある人はサラダと揚げ物、ある人はメインのハンバーグやステーキなどを担当しているとします。思い思いに動いている場合、例えばハンバーグが焼きあがっているのにエビフライが揚がっていないことや、メインディッシュが出来上がっているのにサラダが提供できていないなど、料理の出来上がりの順番や状態がおかしくなってしまうことがあるかもしれません。
非同期処理を組み込むことで時間軸がバラバラになってしまうため、並列化は前後処理や並列処理間での通信などが複雑化しやすくなり、不具合が多くなることもあり得ます。結果として開発コストが増大する・新たなバグが発生するといった事態を招くこともあるため注意が必要となります。
高速化することは重要ですが、高速化を目指したことで処理が終わったとしても、正しくない処理結果になってしまうようなことがあれば本末転倒です。それだったら少しでもスピードを犠牲にして、逐次処理で処理を実行するコーディングにするなどを考える必要が出てきます。
非同期処理の図解イメージ
非同期処理を簡単に図解すると以下のようなイメージです。ここでの非同期処理はシンプルにタスクをワーカースレッドに依頼するイメージです。特定のタスクを複数こなす必要がある場合に、それぞれの処理が独立しているのであれば、ワーカースレッドを活用して処理を分散することができます。
メインスレッドからワーカースレッドを作成して、TaskA の処理を依頼し、さらにワーカースレッドを作成して TaskB を依頼します。メインスレッドはそれぞれのタスクを依頼した後、TaskC に取り掛かり処理を進めていきます。
このパターンはTaskA~C が独立しており、処理がほかの処理に依存していない状態(例えばTaskAの結果がTaskBに影響を与えるなど)の場合になります。処理に依存している場合は、Taskを監視して条件を加えたする必要があるので難しくなります。
非同期処理はムズカシイ
非同期プログラミングは処理の効率性という観点から重要な処理方法ではあるのですが、注意するべきことも増えていくため非常に難易度の高いコーディング技法と言わざるを得ません。効率性が良い反面、不具合が発生しやすくなるのです。
実際、非同期処理をしっかりと理解していないがゆえにデータに不整合が発生し、不具合が発生してお客様先で問題となるようなことにも遭遇してきました。非同期処理に取り組むのは重要ですが、基礎的な考え方も身に着ける必要があることに注意しておきましょう。