競合状態とは何か
競合状態(レースコンディション)とは、複数のプロセスやスレッドが同じデータを同時に書き換えて、結果が実行の順番しだいで変わる不具合です。商品の在庫数や口座の残高のような共有データで起きると、数が合わなくなります。
ネット通販で、在庫が10個の商品を、2人が同時に1個ずつ買いました。在庫は8になるはずです。ところが、9のままになることがあります。
このレッスンのゴール
メモ
競合状態とデッドロックが起きるわけを、説明することができる。
キーワード
| ことば | いみ |
|---|---|
| スレッド | 1つのプロセスの中で動く、1本の流れ |
| 競合状態 | 実行の順番しだいで、結果が変わる不具合 |
| クリティカルセクション | 同時に1つだけが入るべき、共有データを触る部分 |
| ロック | 1つだけが入れるように、入口にかける鍵 |
| デッドロック | 互いに相手を待って、全員が動けなくなること |
| 待ちグラフ | 誰が誰を待つかを矢印で表した図 |
学生が入力するセルは、このプレビューでは空白です。
読んで書く間に割り込まれると壊れる
「在庫を1つ減らす」は、実は3つの手順です。在庫を読み、1を引き、結果を書きます。2人の買い物が、この手順の途中で交互に入ると、片方の書き込みが消えます。
次の表は、最悪の交互の順番です。2人とも、書き込む前に10を読んでいます。
例: 2人が同時に買う
| 順 | 誰が | すること | 在庫 |
|---|---|---|---|
| 1 | A | 在庫を読む(10) | 10 |
| 2 | B | 在庫を読む(10) | 10 |
| 3 | A | 10から1を引いた9を書く | 9 |
| 4 | B | 10から1を引いた9を書く | 9 |
学生が入力するセルは、このプレビューでは空白です。
順番を手で並べて動かす
本物のスレッドは、順番が運しだいで変わります。そこで次のコマンドは、表と同じ順番を、手で並べて動かします。Aが読み、Bが読み、Aが書き、Bが書きます。
在庫は2回減らしたのに、結果は8ではなく9です。毎回、同じ答えが出ます。
更新が消える順番を再現する
python3 -c "stock = 10; a = stock; b = stock; stock = a - 1; stock = b - 1; print(stock)"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
スレッドはメモリを共有する
1つのプロセスの中には、スレッドという流れを、いくつも持てます。同じプロセスのスレッドは、メモリを共有します。だから速く協力できますが、同じデータを書き換え合う危険もあります。
次のコマンドは、ls というプロセスのスレッドの数を数えます。ls は1本だけで動くので、1と出ます。
スレッドの数を数える
ls /proc/self/task | wc -lここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ロックで1つだけを通す
共有データを触る部分を、クリティカルセクションといいます。ここには、同時に1つしか入れません。入口にロックをかけて、持っているものだけが入れるようにします。
持っている間に、別のものが同じロックを取ろうとすると、待たされます。出るときに、ロックを返します。
例: ロックを使った2人の買い物
| 順 | 誰が | すること | 在庫 |
|---|---|---|---|
| 1 | A | ロックを取る | 10 |
| 2 | B | ロックを取ろうとして、待たされる | 10 |
| 3 | A | 読む、引く、書く(9)。ロックを返す | 9 |
| 4 | B | ロックを取る。読む、引く、書く(8) | 8 |
学生が入力するセルは、このプレビューでは空白です。
flockでロックを試す
flock は、ファイルを使ったロックのコマンドです。-n を付けると、取れないときに待たず、すぐ終了コード1で失敗します。
最初のコマンドは、外側でロックを取り、その中で同じロックをもう一度取ろうとします。すでに持たれているので、断られて inner=1 と出ます。
持たれているロックは断られる
flock -n /tmp/osint.lock -c 'flock -n /tmp/osint.lock true; echo inner=$?'ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
誰も持っていなければ取れる
flock -n /tmp/osint.lock true && echo freeここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
デッドロックは互いに待ち続ける
デッドロックは、互いに相手が持つものを待って、全員が動けなくなることです。学生Aが会議室を借りてプロジェクターを待ち、学生Bがプロジェクターを借りて会議室を待つ場面です。
ロックでも同じことが起きます。次の4つがすべてそろったときに起きます。1つでも欠ければ、起きません。
デッドロックが起きる4つの条件
| 条件 | 意味 | 会議室の例 |
|---|---|---|
| 相互排他 | 同時に使えるのは1人だけ | 会議室は1組だけ |
| 保持と待機 | 持ったまま、次を待つ | 会議室を持ってプロジェクターを待つ |
| 横取りなし | 持つものを、無理に奪えない | 借りた部屋は返されるまで使えない |
| 循環待ち | 待つ相手が輪になる | AがBを待ち、BがAを待つ |
学生が入力するセルは、このプレビューでは空白です。
よくある間違い
注意
「ロックを使えば、並行処理の問題はすべて消える」と考える人が多くいます。ロックが1つなら、競合状態は防げます。ただし、ロックが2つ以上あると、取る順序が食い違って、デッドロックになることがあります。