「並行」と「並列」と「非同期」

no extension

面白い記事を見かけたので

今回はこの記事を起点につらつらと書いてみる。

「並行」と「並列」と「非同期」

プログラミングおよびシステム設計に於いて「並行」と「並列」は意味が違うよ,というのはよく言われるし,私も Go 言語関連の話で紹介したことがある。 その一方で「非同期」と「並行」は違うよね,という話は確かにあまり聞かない。 先程挙げた記事では

Asynchrony: the possibility for tasks to run out of order and still be correct.

Concurrency: the ability of a system to progress multiple tasks at a time, be it via parallelism or task switching.

Parallelism: the ability of a system to execute more than one task simultaneously at the physical level.

つまり「並行」と「並列」は能力(ability)だが「非同期」は実行順序の可能性(possibility)であると説明されている。 あるいは順序の自由度の問題と言い換えたらいいだろうか。

ここでプログラミング言語 Zig のコードが登場する。 たとえば Zig による非同期処理は io.async を使って以下のように記述するそうなのだが

// assume that server.listen has already been called
io.async(Server.accept, .{server, io});
io.async(Client.connect, .{client, io});

io.async はシングルスレッドのブロッキングモードで動作するため,上のコード例の場合,一方の処理が他方の処理でブロックされてしまい並列処理にはならないらしい。 この挙動はユーザレベルスレッド1 の特性に近い(Zig の実装が実際にどうなっているかは知らないが)。

ユーザーレベルスレッドの主な利点は性能です。ユーザーレベルスレッドのコンテキストスイッチは、カーネルレベルスレッドのコンテキストスイッチよりも高速です。なぜなら、カーネルレベルのコンテキストスイッチでは、オペレーティングシステムが介入して次に実行するスレッドを選択する必要があるからです。カーネルを呼び出さずに実行を切り替えることができれば、実行中のプロセスは、キャッシュをフラッシュして処理速度を低下させる必要がなく、CPU を保持し続けられます。

ユーザーレベルスレッドを使うことの不都合な点は、ブロッキング I/O 呼び出しを行うコードを実行するときに現れます。ファイルからの読み込みが必要な状況を考えてみましょう。オペレーティングシステムはプロセスを 1 つの実行のスレッドと見なすため、ユーザーレベルスレッドがこのブロッキング読み込み呼び出しを実行すると、プロセス全体がスケジュールから外されます。同じプロセス内に他のユーザーレベルスレッドが存在する場合、読み込み操作が完了するまで、それらのスレッドは実行されません。

正しく並行処理として記述したいなら

try io.asyncConcurrent(Server.accept, .{server, io});
io.async(Client.connect, .{client, io});

という感じにコードで明示する必要があるそうな。

拙文「goroutine はグリーンスレッドではない」でも書いたが Go の goroutine はカーネルレベルスレッドとユーザレベルスレッドのハイブリッドになっていて,非同期かつ並行な処理を記述することが出来る。 なので非同期と並行を分離して考えることはないし,その必要もない(そのぶんランタイム側で制御するスケジューリングが複雑になるんだけど)。 強いて言うなら Goメモリモデルについて注意が必要といったところか。

Zig のように非同期処理と平行処理を明示的に分離して記述するのは面白いと思う2。 より低レベルな処理に気を配らないといけないので,コード設計が面倒くさいっちゃあ面倒くさいんだけどね。

参考図書

photo
Go言語で学ぶ並行プログラミング 他言語にも適用できる原則とベストプラクティス impress top gearシリーズ
James Cutajar (著), 柴田 芳樹 (著)
インプレス 2024-12-04 (Release 2024-12-04)
Kindle版
B0DNYMMBBQ (ASIN)
評価     

読書会のために購入。インプレス社の本は Kindle 版より版元で PDF 版を買うのがオススメ。「並行処理」について原理的な解説から丁寧に書かれている。 Go で解説されているが Go 以外の言語でも応用できる。

reviewed by Spiegel on 2025-01-25 (powered by PA-APIv5)

photo
Goならわかるシステムプログラミング 第2版
渋川よしき (著), ごっちん (イラスト)
ラムダノート 2022-03-23
単行本(ソフトカバー)
4908686122 (ASIN), 9784908686122 (EAN), 4908686122 (ISBN)
評価     

第1版はとてもためになる内容だったので第2版も PDF 版で確保しておく。

reviewed by Spiegel on 2022-10-08 (powered by PA-APIv5)


  1. Asynchrony is not Concurrency” ではユーザレベルスレッドをグリーンスレッドと呼んでいるように見える。なおグリーンスレッドは元々 Java の用語で,しかもバージョンごとに意味が変遷しているらしいので,この記事では「グリーンスレッド」という用語は使わないことにする。詳しくは拙文「goroutine はグリーンスレッドではない」を参考にどうぞ。 ↩︎

  2. Async Rust Is A Bad Language” では Rust における非同期処理の問題点が挙げられていて,最後に “Maybe Rust isn’t a good tool for massively concurrent, userspace software” とまで書かれていて笑ってしまった。みんな苦労してるんだなぁ。 ↩︎