コンテンツにスキップ

ライブロックとは:デッドロックとの違いと5つのポイント

概要

ライブロック(Livelock)とは、スレッドがブロックされていないにもかかわらず、互いに反応し合って永遠に進歩できない状態のこと。デッドロックと混同しやすいが「動いているのに進まない」という点が異なる。ランダムバックオフや再試行制限で解消できる。

詳細

デッドロック vs ライブロック

特性 デッドロック ライブロック
スレッドの状態 ブロック(停止している) 実行中(動いている)
CPU消費 低い(待機中) 高い(アクティブに動いている)
進行 なし なし
検出難易度 比較的検出しやすい 発見しにくい
典型例 ミューテックスの循環待ち 互いに譲り合い続けるスレッド

ライブロックのイメージ

「廊下で正面衝突した2人が、お互い同じ方向に避け続ける」

  Thread A: 「どうぞ」→ 右に避ける
  Thread B: 「どうぞ」→ 右に避ける
  Thread A: 「どうぞ」→ 左に避ける
  Thread B: 「どうぞ」→ 左に避ける
  ...永遠に繰り返す(礼儀正しすぎて誰も通れない)

発生しやすいコンテキスト

1. ロックフリーコード(ブロッキングなし、アトミック操作)
   → CASループで互いの変更を上書きし合い続ける

2. 楽観的同時実行制御(Optimistic Concurrency)
   → 「競合なしを期待、衝突したらリトライ」の設計で
     無限リトライになる

Go での例(ライブロックが発生しうるパターン)

// 悪い例:固定の再試行でライブロックになりうる
for {
    if tryAcquireResource() {
        break
    }
    // 毎回同じタイミングで競合すると永遠に解消しない
    time.Sleep(10 * time.Millisecond)
}

// 良い例:ランダムバックオフで競合を回避
for i := 0; i < maxRetries; i++ {
    if tryAcquireResource() {
        break
    }
    // ランダム要素を加えることで競合を分散させる
    jitter := time.Duration(rand.Intn(100)) * time.Millisecond
    time.Sleep(10*time.Millisecond + jitter)
}

5つのポイント(まとめ)

1. ライブロック = スレッドが互いに反応し合い、進歩がない状態

2. 例え:「どうぞ」→ 礼儀正しすぎて誰も通れない
         誰も前に進めない

3. スレッドはリトライし続ける → 永遠に譲り合い

4. 発生しやすい場所:
   - ロックフリーコード(アトミック操作)
   - 楽観的同時実行制御(衝突したらリトライ)

5. 解決策:
   - ランダムバックオフを追加する
   - リトライ回数を制限する
   - 調整機構(コーディネーター)を使う

なぜ重要か / いつ使うか

  • 並行処理の面接で「デッドロックとライブロックの違いは?」と聞かれたとき
  • ロックフリーなデータ構造を設計するときの落とし穴として
  • CASループや楽観的ロックを実装するときの注意点として