Mímisbrunnr知恵の泉

← コンピュータ基礎 一覧

🎓 レベル:標準 | 重要度:B(推奨)

📎 前提:排他制御(ロック・セマフォ・ミューテックス) | 関連:デッドロックプロセス間通信(IPC)

要点(BLUF)

概念 ── 同期には「排他」と「待ち合わせ」の2種類がある

競合状態とクリティカルセクション で見た相互排他は「同時に触らせない」仕組みでした。しかし実務では、もう1つ「条件が整うまで眠って待つ」仕組みが要ります。例えば「キューが空なら、何か入るまで消費側は待つ」。

この「待ち合わせ」を担うのが条件変数(condition variable)セマフォ。ロックだけでビジーに条件をチェックし続けると(スピン)CPUを浪費するので、「条件が変わるまで眠り、変わったら起こす」が要ります。

仕組み① ── 生産者消費者(有界バッファ)

データを作る生産者と使う消費者を、固定容量の有界バッファで繋ぎます。パイプ(プロセス間通信(IPC))の本質もこれです。

要件は2つの待ち合わせ:

flowchart LR
    P["生産者(put:満杯なら待つ)"] -->|"アイテム"| BUF["有界バッファ(容量N)"]
    BUF -->|"アイテム"| C["消費者(get:空なら待つ)"]

セマフォを2つ使う古典解:empty(空き枠数、初期N)と full(中身数、初期0)。生産者は empty を1減らして入れ full を1増やす、消費者は逆。バッファ本体の同時操作はミューテックスで守ります。これで「枠の数」をセマフォが自然に管理します(排他制御(ロック・セマフォ・ミューテックス) のセマフォがカウンタを持つ理由)。

実機で確かめます([[#対応ラボ]] の 04-04_producer_consumer.py。Pythonの queue.Queue が満杯/空のブロックを内蔵)。

BUF = queue.Queue(maxsize=3)     # 有界バッファ
def producer():
    for i in range(N_ITEMS): BUF.put(i)   # 満杯なら自動でブロック
    BUF.put(None)                          # 終了の番兵
def consumer():
    while True:
        item = BUF.get()                   # 空なら自動でブロック
        if item is None: break

実行結果(実機):

生産: [0, 1, 2, 3, 4, 5, 6, 7]
消費: [0, 1, 2, 3, 4, 5, 6, 7]
全アイテムが順序通り過不足なく渡った: True

容量3のバッファなのに8個を過不足なく順序通り受け渡せたのは、満杯で生産者が、空で消費者が自動的に待ったから。これが速度差のある2者を繋ぐフロー制御です。

仕組み② ── 読者書き手問題

共有データに対し、読むだけの読者は何人いても同時OK(互いに干渉しない)だが、書き手は排他(読者とも他の書き手とも同時不可)。単純な1本のミューテックスだと読者まで直列化して並列性を捨ててしまうので、専用の**読み書きロック(RWロック)**を使います。

flowchart TB
    subgraph OK["同時に許される"]
      r1["読者"]; r2["読者"]; r3["読者"]
    end
    subgraph NG["排他が必要"]
      w["書き手(単独・読者もブロック)"]
    end

設計の肝は公平性。読者を無制限に通すと、読者が途切れず書き手が永久に入れない(書き手の飢餓)。逆に書き手優先にすると読者が飢える。実装はどちらを優先するか方針を決めて偏りを防ぎます(飢餓は デッドロック と並ぶ並行バグ)。

仕組み③ ── 条件変数の使い方の定石

条件変数は「ロックを保持しつつ、条件が満たされるまで眠る」道具。定石はwhileで条件を再確認すること。

with cond:                 # ロックを取る
    while not 条件():       # if でなく while(起こされても条件が崩れていることがある)
        cond.wait()        # ロックを手放して眠り、起こされたら取り直す
    # ここに来たら条件成立。共有データを操作

if でなく while なのは、**偽の起床(spurious wakeup)**や、起こされた後に別スレッドが先に条件を崩す可能性があるため。起きたら必ず条件を確かめ直す――これを怠ると稀に壊れます。

仕組みの直観 ── なぜこの設計か

⚠️ よくある誤解・落とし穴

対応ラボ

cs-foundations-study/labs/04-04_producer_consumer.py(実行して有界バッファの受け渡しとブロックを確認済み)。

関連

第4章 並行処理と同期 目次