デッドロック — お互いの一足を待ち続ける2人
2人のランナーが、それぞれ片方ずつスニーカーを握っています。Aさんは右足、Bさんは左足。走るには左右ペアが必要なのに、お互い「君がそっちを離してくれたら走れるのに」と相手の1足を待ち続け、永遠に動けません。これがデッドロックです。
プログラムでも、複数の処理が複数の資源(ロック)を取り合うとき、まったく同じことが起こります。下の図1で、2つのスレッドにロックを取らせて、どうすれば膠着するのか・どうすれば防げるのかを自分の手で再現してみてください。
ロックA 空き
ロックB 空き
スレッド①
待機中
スレッド②
待機中
図1 — 2つのスレッドにロックを取らせて、膠着を再現する
デッドロックが成立する「4つの条件」
デッドロックは気まぐれに起きるのではなく、4つの条件がすべて揃ったときだけ発生します(コフマンの条件)。図1で起きたのは、まさにこの4つが同時に成り立った瞬間です。
① 相互排他ロックは同時に1人しか持てない
② 保持と待機1つ持ったまま、次のものを待つ
③ 横取り不可持っているロックを奪い取れない
④ 循環待ち①は②を、②は①を待つ輪ができる
裏を返せば、この4つのうち1つでも崩せばデッドロックは起きません。実務でいちばんよく使われるのが④「循環待ち」を壊す方法です。
やり方は驚くほど単純で、「全員が必ず同じ順番でロックを取る」と決めるだけ。たとえば「必ずA→Bの順で取る」と統一すれば、スレッド①がAを持っているとき、スレッド②はそもそもBより先にAを取りに来るので、Aの前で待つことになります。①→②→①という待ちの輪(サイクル)ができなくなるのです。図1で②にも「Aを取得」から始めさせると、デッドロックにならないことを確かめてみてください。
用語ミニ辞書
- ロック / Mutex
- 資源を「同時に1人だけ」使わせるための鍵。Mutexはmutual exclusion(相互排他)の略。
- 循環待ち
- AはBを、BはAを待つ、というように待ち合いが輪になった状態。デッドロックの核心。
- ロック順序
- 「必ずこの順で取る」という取得順の取り決め。循環待ちを防ぐ定番の対策。
まとめ
デッドロックは「お互いが、相手の持っているものを待ち続ける輪」。発生には4条件が揃う必要があり、逆に1つ崩せば防げます。実務では「ロックを取る順番を全員で統一する」のが王道。並行処理のバグで処理が固まったら、まず「どこかに待ちの輪ができていないか」を疑ってみてください。