ITパスポート 2012年 秋期 問80
問題文
ある在庫管理システムは、複数の入力を同時並行して処理し、在庫数を更新しているが、排他制御は行っていない。ある時点での在庫数が100であったとき、入力された二つの入力A、Bに応じて、図に示す処理が①→②→③→④の順序で実行された場合、処理④が終了した時点の在庫数は幾つになるか。

選択肢
ア:70
イ:100
ウ:120
エ:150(正解)
🔒 解説は解答すると表示されます
在庫管理の同時更新で起きる「取りこぼし」問題【ITパスポート 解説】
正解の理由
この問題では、在庫数(初期値100)を同時に更新する2つの処理があり、排他制御(ほかの処理と同時に共有資源を更新しない仕組み)は行われていません。処理の実行順序が ①→②→③→④ のとき、入力Aが①で「在庫数」を読み取り(100)50を加算して150を計算しますが、まだ書き込みは行いません。一方で入力Bが②で同じ「在庫数」を読み取り(これも100)、30を引いて70を計算し、③でその70を書き込みます。最後に入力Aが④で自分の計算結果(150)を書き戻すため、最終的な在庫数は150になります。したがって正しい選択肢は エ(150)です。
(用語補足)
- 排他制御(exclusive control):同じデータを同時に操作しないようにする仕組み。
- 競合状態(race condition):複数の処理が同じ資源を同時に扱うことで誤った結果になる状況。
解法ステップ
- 初期の在庫数 = 100。
- ①(入力Aの読み込み): Aが在庫数を読み込む → 読んだ値 = 100。Aは50を加えた結果を内部で保持(計算結果 = 150)。まだ書き込みなし。
- ②(入力Bの読み込み): Bが在庫数を読み込む → 読んだ値 = 100(Aの書き込みはまだ)。Bは30を引いた結果を内部で保持(計算結果 = 70)。
- ③(入力Bの書き込み): Bが在庫数に70を書き込む。この時点で在庫 = 70。
- ④(入力Aの書き込み): Aが自分の計算結果150を書き込む。最終的な在庫 = 150。
まとめると、
- 並列処理で読み込みが重なると、後から書き込みをした処理が前の結果を上書きする(取りこぼし)が起きます。
- この問題の順序では A の書き込みが最後に行われるため最終値は150。
選択肢別の誤答解説
-
ア: 70
Bの更新(70)が最後に残るケースを想定した答えです。しかし実際には最後にAが150を書き戻しているため最終は70にはなりません。 -
イ: 100
どちらの更新も反映されていないと仮定した答えです。読み込みだけで書き込みが無かった場合は100のままですが、この図では③および④で書き込みが行われるため当てはまりません。 -
ウ: 120
これは「正しい直列実行」を想定した結果です。通常、もし A と B が順序通り(例えばAが先に完全実行、次にBが実行)で行われれば になります。しかし今回の実行順序(読み込み・書き込みが混在)では取りこぼしが発生し、120にはなりません。 -
エ: 150
Aが最後に150を書き戻すため、最終的に在庫が150になります。これがこの問題の正解です。
よくある誤解
-
「同時に処理しても合計は足し引きされる」と思う誤解
実行の順序(特に読み込みと書き込みのタイミング)によっては、ある更新が別の更新で上書きされ、期待する合計と異なる結果になります。単純な「同時=足し算・引き算が合算される」ではありません。 -
「読み込み時点の値は常に最新」と思う誤解
読み込んだ値はその瞬間の値です。別の処理がその後に書き込むと、その読み込みを基にした計算は古い値に基づく結果になります(いわゆるスタレーデータ=古いデータ)。
補足コラム
この問題で起きているのは「競合状態(race condition)」による「取りこぼし(lost update)」です。実システムでは、これを防ぐために以下のような対策が使われます(簡単な説明付き):
- ロック(mutex, ミューテックス): 処理が共有データを扱うときにロックを取って他の処理を待たせる方法。確実だが待ち時間が発生する。
- トランザクション(transaction: まとまった一連の処理を一つとして扱う仕組み)と排他制御: データベースでよく使われる。失敗時はロールバック(元に戻す)できる。
- 楽観ロック(optimistic locking): 更新時に「自分が読み込んだときから変わっていないか」をチェックして、変わっていたら再試行する方式。競合が少ない場合に効率的。
- 原子的操作(atomic operation)/ 一括更新: 読み出し・計算・書き込みを分離ではなく一つで行う(例:SQLの UPDATE inventory SET stock = stock + 50 のようにDB側で直接加算する)。
簡単なコード例(排他制御なしの疑似Python):
# 共有変数
stock = 100
def inputA():
global stock
v = stock # 読み込み
v = v + 50 # 計算
stock = v # 書き込み
def inputB():
global stock
v = stock
v = v - 30
stock = v
このように読み書きが分かれていると、並行実行で取りこぼしが起きます。
FAQ
Q1. 排他制御って必ず必要ですか?
A1. すべての場合に必要というわけではありません。複数の処理が同じデータを同時に更新する可能性がある場合は必要です。読み取りだけであれば通常は不要です。業務上の正確性が重要なら排他制御を検討します。
A1. すべての場合に必要というわけではありません。複数の処理が同じデータを同時に更新する可能性がある場合は必要です。読み取りだけであれば通常は不要です。業務上の正確性が重要なら排他制御を検討します。
Q2. データベースを使えば自動で安全になりますか?
A2. データベースはトランザクションやロックなどの仕組みを提供しますが、使い方次第です。単純にアプリ側で読み出して計算して書き戻すと、今回のような問題は起きます。DB側で「UPDATE ... SET stock = stock + 50」のように一括で処理するか、トランザクションで適切な隔離レベルやロックを設定する必要があります。
A2. データベースはトランザクションやロックなどの仕組みを提供しますが、使い方次第です。単純にアプリ側で読み出して計算して書き戻すと、今回のような問題は起きます。DB側で「UPDATE ... SET stock = stock + 50」のように一括で処理するか、トランザクションで適切な隔離レベルやロックを設定する必要があります。
Q3. 実務でよく使われる対策は何ですか?
A3. 小規模で競合が多い場合はロックやトランザクションを使います。競合が稀なら楽観ロック(更新時にバージョンを検査して失敗したら再試行)を使うことが多いです。システムによって最適な方法は変わります。
A3. 小規模で競合が多い場合はロックやトランザクションを使います。競合が稀なら楽観ロック(更新時にバージョンを検査して失敗したら再試行)を使うことが多いです。システムによって最適な方法は変わります。
関連キーワード: 排他制御、競合状態(race condition)、ロック、トランザクション、楽観ロック、取りこぼし、原子性、相互排他、データ整合性

\ せっかくなら /
ITパスポートを
クイズ形式で学習しませんか?
クイズ画面へ遷移する→
すぐに利用可能!

