Think Stitch
  最近の更新


なぜ pthread_cond_wait はミューテックスを引数にとるのか

pthread_cond_wait をはじめてみたとき,どうして条件変数を待つのにミューテックスを渡す必要があるのだろうと思った人がいるのではないかと思います.私はそう思いました.

pthread_cond_wait 関数の説明によれば,ミューテックスのアンロックと条件変数への待ち状態入りは不可分に行われるとあります.マルチスレッドが起こすいろいろな問題を見てきたいまでは,不可分にしないとその間に別のスレッドが動いておかしなことになるのだろうくらいの想像はできます.でも具体的には?今回はこれを調べてみたいと思います.

Pthread Model Checker には pthread_cond_wait の動作が組み込まれています.モデルを記述するだけでこれを変えることはできないので,今回は Pthread Model Checker を改造して,ミューテックスのアンロックと条件変数への待ち状態入りが不可分ではない,特別版の Pthread Model Checker を作ってみました.

例題としては生産者・消費者問題を使います.まず改造していない Pthread Model Checker で状態遷移図を生成してみると,生産者は次のようになります.状態 32 から続く wait, lock という2つの遷移が pthread_cond_wait に対応します.内部で使われている wait は pthread_cond_wait と違って再ロックしないからです.

同じモデルを改造版 Pthread Model Checker で処理してみると次のようになります. pthread_cond_wait が今度は3つの遷移 unlock, wait-non-atomic, lock に別れました.

消費者も同様です.(生産者の方が図を切り出しやすかっただけです^^;)

これで検査を実行してみるとデッドロックが発見されます.パスのサマリは次のとおりです.

#10 pthc[0] lock mutex
#11 pthc[0] load count
#13 pthc[0] unlock mutex                (1)

#15 pthp[0] lock mutex
#18 pthp[0] load count
#19 pthp[0] store count
    count = 1
#20 pthp[0] signal cv2
#21 pthp[0] unlock mutex

#22 pthp[0] lock mutex
#25 pthp[0] load count
#26 pthp[0] store count
    count = 2
#27 pthp[0] signal cv2
#28 pthp[0] unlock mutex

#29 pthp[0] lock mutex
#30 pthp[0] load count
#35 pthp[0] unlock mutex
#37 pthp[0] wait-non-atomic cv          (2)

#38 pthc[0] wait-non-atomic cv2         (3)

まず消費者が動きます.ロックをかけて count を見たら 0 だったので wait に入ろうとします. (1) で unlock をして,次に wait-non-atomic を実行しようとしますが,そこで生産者が割り込んできます.

生産者は値を2つ生産し,バッファがフルになって wait に入ります (2).この間に発行された signal は受け手がいないのですべて空振りになります.

そのあとでやっと消費者が wait-non-atomic を実行して,結果デッドロックしてしまいました.もし unlock と wait-non-atomic が不可分だったら signal を取りこぼすことはなかったわけです.

このようにミューテックスのアンロックと条件変数への待ち状態入りが不可分でない場合,その間で別のスレッドが動いて signal を取りこぼすことがあるのでまずいわけです.不可分にするためには条件変数だけでなくミューテックスも受け取らなければならないわけですね.

2017/10/15

© 2013-2017 PRINCIPIA Limited