クリティカル・セクション

ある1テーブルにレコードを登録(挿入および更新)するプログラムを書きます。
キーが同一のレコードを重複して挿入してはなりません。
この機能をマルチスレッドで動かしたとき、複数スレッドから同一キーでレコードを登録する可能性があるとして、スレッドセーフにするには、どう制御するのがよいでしょうか。
更新を防ぐには、DBの当該レコードを先に読んだトランザクションから、排他ロックをかけてしまえばよいでしょう。しかし、当該レコードがない状態で並行して2スレッドが同一キーのレコードを挿入してきた場合には、以下のような可能性も否定できません。

  スレッド1                              スレッド2
+------------------------------------------------------------------
  テーブルを読む

  当該キーに対応する
  レコードなし
                                         テーブルを読む

                                         当該キーに対応する
                                         レコードなし
 
  当該レコードを登録

                                         当該レコードを登録
                                        (キー重複)

上記シナリオのように、別スレッドが再入することでデータの一貫性が破壊されるため、1まとまりの処理として非並行的(逐次的)に実行しなければならない局面のことを、クリティカル・セクション*1と言います。
Javaであればsynchronizedというキーワードがありますね。
マルチスレッド環境というのは、時に、count++さえ排他制御なしに,複数のスレッドでこれを同時に実行すると,「百回インクリメントしたはずなのに1しか増えていない」ということも起こりうる*2のでありました。
最近、本番環境のトラブル(上述した業務キー重複の検出)で帰ろうとしたところを呼び止められたことが度々あって若干ショックを受けていますのでちょっと書いておきました。

*1:参考リンク:クリティカルセクション

*2:Javaについては、http://www.netgene.co.jp/java/concurrentTips.html#atomic。また、Obj-Cについて解説しているhttp://homepage.mac.com/mkino2/spec/optimize/refresher.htmlという文書がありました。