async/awaitに見る同期/非同期とblocking/non-blocking
シングルスレッドやマルチスレッドプログラミングで、同期/非同期と共に良く目にするキーワードに、blocking/non-blockingがある。
組み込み屋には、blocking/non-blockingの方が馴染みがあって、
「なんとなく同期 = blocking」
「non-blockingは直ぐに制御が戻ってきて進捗モニタして完了を判断しないといけない」
「非同期はコールバックで戻ってくるんだっけ?」
程度の感覚しかない。
async/awaitをサラッと流して、裏の実装を理解していない時は、async/awaitがスレッドの処理を止めないというのがピンとこなかった。
「awaitでコンテキストスイッチしてないの?」
「イベント駆動で同一スレッドの記述が分散している中で、一旦処理を抜けてコールバックしてくるってどういうこと?」
ざっくり表現すれば、そういう疑問・・・
組み込みOSもいろいろあるが、自分はコンテキストスイッチのタイミングを自力で正しく実装しなければデッドロックするようなシステムで開発していた。
組み込みで実装すると、いろいろやり方はあるが、例えばこんな感じ・・・
- non-blockingコールを呼んで処理をスタート
- 完了フラグを待つ (コンテキストスイッチが発生)
- 別スレッドで処理を実行
- 別スレッドでポーリングして進捗チェック
- 完了したら完了フラグを立てる
- コンテキストスイッチを発生させる
- 呼び出し元へコンテキストが戻る
白状すると、
「そもそもローテクの組み込みではコールバックとか使ったことない・・・」
一応、同期/非同期とblocking/non-blockingについて調べてみると、
と、そのリンク先の説明が詳しかった。
同期/非同期とblocking/non-blockingは別の定義で、組み合わせると4パターンになる」
ということは、覚えていてよさそう。
さて、本題ですが、じんぐる大先生のサイトに以下の資料がある
その中の「内部実装を覗く」に自分の求めるものがあった・・・
実体は「ポーリングして進捗チェック」
つまり、non-blockingで状態監視のパターン
完了したら制御が戻る (= 登録されている続きの処理をする)
継続処理に自分自身を登録することで、不要なコンテキストスイッチを回避
なお、
TheC10kProblem - 「C10K問題」(クライアント1万台問題)とは、ハードウェアの性能上は問題がなくても、あまりにもクライアントの数が多くなるとサーバがパンクする問題のこと
には、エッジトリガとレベルトリガの話も出ている。
ソフトウェアの世界では
- イベント通知がエッジトリガ
- 「ポーリングで進捗チェック」がレベルトリガ
と考えておいたら良い。
async/awaitは、
「レベルトリガをエッジトリガに変換しているのだな」
と納得。