Skip to content

データ競合とは

データ競合は、次の三つが同時に成り立つときに発生します。

  • 複数の処理が、同じ状態へ同時にアクセスする
  • 少なくとも一方の処理が、その状態に書き込み処理をする
  • それらのアクセスが同期されていない

複数の処理が同じデータを読み取るだけなら、データ競合にはなりません。一方が書き換える場合は、読み取りと書き込みが同時に行われないように、同期してあげる必要があります。 そして、これができていない場合にデータ競合が発生します。

読み取り同士ならデータ競合は起きない。読み取りと書き込みが同期されずに同時に起きるとデータ競合になる。

例えば、書き換え可能なCounterを二つのTaskで共有する場面を考えます。

final class Counter {
var value = 0
func increment() -> Int {
value = value + 1
return value
}
}
func runExample() {
let counter = Counter()
Task.detached {
print(counter.increment())
}
Task.detached {
print(counter.increment())
}
}

increment()は、valueを読み取り、1を足し、その結果を書き戻します。二つのTaskが同時に実行すると、両方が0を読み、両方が1を書き戻す可能性があります。 このようなデータ競合が実際に起こった場合にはどうなるでしょうか。 実際には、以下のようにエラーが発生してクラッシュしたり、不正なデータが発生してしまいます。

データ競合で起きたクラッシュ。Thread 1: EXC_BAD_ACCESS (code=1, address=0x48)