(2018/11/11 23:19追記あり)
Linuxでpthreadを使っている。
スレッドがいくつかあって、相手のスレッドに処理要求して、終わるまで待つ、という同期処理が必要になった。
そういうときは、pthread_cond_wait()で待って、pthread_cond_signal()で起こしてもらえば良かろう。
・・・ということはわかったのだが、あまり使い方を読まずに使っていた。
そのせいで、相手のスレッドに要求するスレッドが複数になった場合にmutexがlockするという現象が起きていた。
ちゃんと読まずに使うのはよくない、という反省を込めて記事にしよう。
共有のメモリに処理してほしい内容を通知して、相手は処理したらそのメモリに結果を返す。
処理する方は呼ばれるまで待っているし、呼び出す方のスレッドは終わるまで待たせる。
pthread_cond_wait()は引数にlockしたmutexを取るようだったので、こう考えていた。
- cond_wait()する直前にmutex_lock()する
- mutex_loc()するんだから、他のスレッドが同じことをしようとしたらmutex_lock()で待たされる
そして、こういう実装をしていた。
見づらくてすまんが、黄色から右側が処理するスレッドで、左側は要求するスレッドたちだ。
要求するスレッドは、まず要求側のmutexをlockして、メモリに書込み、cond_signal()で相手を起こしたら、自分はcond_wait()で寝る。
処理側は処理が終わったらcond_signal()で起こして、自分はcond_wait()で寝る。
これでよかろうと思っていたのだが、何だかよくわからないタイミングでスレッドが固まることが分かった。
pthread_cond_wait()の使い方など疑ってもいなかったのだが、他にあやしい箇所がない。。。
IBM: 一般的なスレッド: POSIX スレッドの説明: 第3回
これの「もう一度、復習を」を見ると、pthread_cond_wait()呼び出しはロックを解除した後にスリープ状態になる、と書かれている。
そ、そんなばかなー!
よく読むと、こういうことらしい。
- pthread_cond_wait()は、mutexをunlockした後でスリープ状態になる
- pthread_cond_signal()などが呼ばれると、mutexをlockしてから起き上がる
今回の現象は、pthread_cond_wait()のあとはunlockされているから、別のスレッドがlockできてしまうために起きているのだ。
実装したものがすべてじゃないのね・・・。
そういうことなので、シーケンスを修正した。
明示的に実装していないmutexのlock/unlockは赤い矢印で表した。
これなら、mutexのlock/unlockが意図せずクロスすることはないだろう。
いやー、mutexというかpthreadは内部の動きも理解していないといけませんな。
などと書きつつも、これも間違っていたら嫌だなぁ。。。
2018/11/11 23:19追記
やはり・・・間違えていた。
基本的な考え方は悪くなかったのだが、シーケンスが良くない。
描いたシーケンスだと、
- 右側のスレッドがcond_wait()する
- 左側のスレッドがcond_signal()を投げる
- 左側のスレッドがcond_wait()する
- 右側のスレッドがcond_signal()を投げる
としていたのだが、2番でsignalを投げた後、左側がwaitに入る前に右側のスレッドがsignalを投げてしまい、待ち人来たらずになってしまうことがあったのだ。
そもそも、cond_wait()の前にmutex_lock()することで何がよいかというと、cond_wait()されることでmutex_unlock()が行われることだ。
これによって、別のスレッドがmutex_lock()して待ち状態になっているものが解除されることで、順番を制御できるのだ。
大ざっぱに書くと、こういう感じか。
先にlockして、signalを投げたい方をlockで待ち状態し、waitでunlockすると、相手がsignalを投げるのは必ずwaitした後になるということだ。
修正は1箇所。
左側のスレッドがsignal()を投げた後にlock()していたのを、その前にするだけだ。
そうしておくと、右側のスレッドはsignal()を投げる前にlockしているので、順番が守られる。
さあ、これくらいで直ってくれないだろうか。。。
0 件のコメント:
コメントを投稿
コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。
注: コメントを投稿できるのは、このブログのメンバーだけです。