前回のブログでは、排他処理区間を絞ると同時に2つのミューテックス変数を使うように改造した結果、再びプログラムが終わらなくなりました。今回はこの問題の調査と対策を行います。
終わらない並列処理の原因調査
まず、前回の『シングルスレッドで試す』に習って、mutex変数を1つにして動かしてみました。
上記のように片方をコメントアウトし、2つの関数が使用するミューテックス変数をひとつにしたところ、全く問題なくなりました。
しかし、2つの関数を比べると気になるとことがあります。ミューテックス変数のロック順が逆になっています。
使う変数の順に合わせてロックしているのですが、プログラムが停止する原因はここにあります。
printf()を使用して、どこで停止しているのか確認してみます。
コメントを外して、2つのミューテックス変数を使用するように戻し、マクロを書き換えて、ミューテックスのロック・アンロック処理に情報出力のprintf()を入れます。
このソースのプログラムを動かすと、topとendの両関数がひとつめのミューテックス変数をロックしたところで停止しています。
ミューテックス変数のunlock待ち状態、デッドロック ( deadlock )
両関数は相手と別の順で2つのミューテックス変数をロックしようとするため、お互いがタイミング良く(悪く)片方のミューテックス変数をロックした時に、相手のアンロック待ち状態に陥ってしまいました。
このような状態で両関数が動けなくなったことをデッドロックと呼びます。
デッドロック状態にあるプログラムについて、今回も gdb で確認しました。
2つの並列動作関数は、スレッド 6 と 7 で動いています。
top関数が76行目、end関数は101行目で止まっています。
問題の解決方法はロック順を揃えることです。
両関数のミューテックス変数ロック・アンロック順を揃えることで、このプログラムが正常に動作するようになりました。複数のミューテックス変数を使う際にロック順を決めておかないと、このような問題が発生します。
ところで、このプログラムで使用しているミューテックス・ロック関数は、pthread_mutex_lock() です。
この関数は指定のミューテックス変数がロック状態の時に、ロック中の関数がアンロックして自身がロックできるまで戻ってきません。そのために、デッドロックが発生したのですが、ロック中かどうかを確認する pthread_mutex_trylock という関数があり、こちらを使用するとロック状態を知ることができるのでデッドロックを起こさないような工夫ができます。しかし、失敗するとライブロックを発生することにもなります。
次回は、pthread_mutex_trylock を使ってライブロックを起こしてみます。
コメントをお書きください