CSAPP 第12章 並行プログラミング
プロセスによる並行プログラミング
fork, exec, waitpidを使ってプロセスを作成し、並行を実現する。
2つのクライアント、1つのサーバーという状態を考える。
- クライアントNo.1からサーバーはリクエストを受ける(受付ディスクリプタはfd = 3とする)
- サーバーはconnection用のディスクリプタを作成する ex) fd = 4
- サーバーはforkで子プロセスを作る。以降クライアント1はfd = 4を持つ子プロセスで処理を行う。
- 親プロセスはfd = 4を閉じ、子プロセスはfd = 3を閉じる。これによって待受fdは以前として3であり、複製プロセスは4となる
- クライアントNo.2からリクエストを受け取る
- サーバーはconnection用の先ほどとは異なる、ディスクリプタを作成する。 ex) fd = 5
- 先程と同様に、サーバーはforkをして子プロセスでクライアント2の処理を開始する。
プロセスはアドレス空間を共有しないため、親プロセスのある処理が子プロセスの処理に影響を及ぼすということがないのが特徴である。
これにより良くも悪くもお互いが干渉しない並行プログラミングができる
select
2つのsocketを使用しているときに、どちらかのsocketに対してinterruptするときに使用する
マルチプレクサ
一つの線で複数のやり取りができるように見せかけること
並行イベント駆動プログラミング
あるイベントの発火、IOデバイスによる入力などにおける割込などが発生したとき、それに基づくプログラムが起動する。
このプログラムは上から順番に実行されるプログラムとは異なり、制御フローが定まらないため競合などがおきないようにプログラムする必要があるので難しいが、自由度は他の並行プログラミングよりも高い
スレッド
単一プロセス内部でリソース空間を分割する技術
メインスレッドとピアスレッドに分割され、スレッドコンテキストスイッチによって処理を移動する。
プロセスコンテキストスイッチよりも移動速度が早い
auto 変数
普通の変数
セマフォ(semaphore)
排他制御のアルゴリズム
非負グローバル変数を定義して、PとVによって各スレッドの干渉を防ぐアルゴリズム
Pはproberen(テストする)、Vはverhogen(加算する)から来ている
基本的な動作としてはクリティカルセクションをPとVで囲む事によって実現する。
P(s)
cnt++
V(s)
semaphoreが0の時、スレッドの飽きがない状態。
0以外のときスレッドの飽きがある状態を表す。
Pによって現在のセマフォの値をチェックする。0以外のときはPの後の処理は競合しないと判断して、スレッド処理を開始する。もし0のときは他のスレッドの終了を待つ必要がある。
そのため、Pで処理を待機する。
Vによってスレッド処理の終了を実現する。V()によりセマフォを加算する。
そうすることで、もしセマフォが0だった場合、セマフォは1になる。
これにより、Pでチェックしていたセマフォが != 0となるのでPで待機していた側のスレッドの処理を開始することができる。
上記のように、スレッド共有変数を保護するために使われるセマフォは0と1で表現されるので、バイナリセマフォと呼ばれ、これはミューテックス(mutex)とも呼ばれる。
mutexに対してP操作を行うことは、mutexをロックすると呼ぶ。
mutexに対してV操作を行うことは、mutexをアンロックすると呼ぶ。
mutexをロックしたが、まだアンロックしていないスレッドはmutexを保持しているという。
バイナリで管理するのではなく、値のカウンタとして使われるセマフォは係数セマフォと呼ぶ。
並列処理は現代のマルチプロセッサが普及している時代において、マルチコアでの処理を実現することができるためとても有用である
スレッドセーフ
スレッドセーフとは、マルチスレッド環境において、あるコンピュータプログラムを複数のスレッドで並行して実行しても、問題が生じない仕様や設計になっていること。
関数によっては、スレッドアンセーフは関数の代わりにスレッドセーフの関数を持つ関数群が存在する。
rand →rand_r
gethostbyaddr → gethostbyaddr_r
suffixに_rを持つ関数はスレッドセーフである
リエントラント
リエントラントとは、あるプログラムやルーチンなどを実行中に、再び起動して実行し始めることができる性質。多重に起動されても狂いが生じず正しく動作する設計になっていることを表す。
つまりスレッドセーフな関数の中でも特にスレッドセーフなものを指す。
スレッドセーフな関数は同期処理などで待機などが必要なのに対し、リエントラント関数は親の呼び出しなどを考慮せず使用する事ができる。
→というのは嘘で暗黙的リエントラントの場合は考慮しないといけない
デッドロック
Pによる待機に対するVが存在しない時、そのプログラムの処理はPの処理の待機から抜けられない。
この並行プログラムにおける設計ミスをデッドロックと呼ぶ
並行プログラミングは並行にするごとに効率が上昇するというわけではない。
マルチコアの数や最適化によってその処理速度は変動する。
そのためいつでもベストプラクティスであるとはいえない。