NOTE 同時実行またはマルチスレッドコードを作成したことがある場合、メモリへのアクセスの競合はよくある問題かもしれません。ただし、ここで説明するアクセスの競合はシングルスレッドでも発生する可能性があり、同時実行またはマルチスレッド化されたコードは関係しません。シングルスレッド内からメモリへのアクセスが競合している場合、Swift はコンパイル時または実行時にエラーを確実にスローします。マルチスレッドコードの場合は、Thread Sanitizerを使用して、スレッド間で競合するアクセスを検出します。
stdatomic(3)
のマニュアルページを参照してください。stepSize
はグローバル変数で、通常は increment(_:)
内からアクセスできます。ただし、stepSize
への読み取りアクセスは、number
への書き込みアクセスと重複します。次の図に示すように、number
と stepSize
は両方メモリ内の同じ位置を参照します。読み取りと書き込みアクセスは同じメモリを参照し、それらが重複して競合が発生します。stepSize
の明示的なコピーを作成することです。increment(_:)
を呼び出す前に stepSize
のコピーを作成すると、copyOfStepSize
の値が現在の stepSize
によって増分されることが明らかです。書き込みアクセスが開始される前に読み取りアクセスが終了するため、競合はありません。balance(_:_:)
関数は、その 2 つのパラメータを変更して、合計値をそれらの間で均等に分割します。playerOneScore
と playerTwoScore
を引数として呼び出しても競合は発生しません。時間的に重複する 2 つの書き込みアクセスがありますが、メモリ内の異なる位置にアクセスします。対照的に、両方のパラメータの値として playerOneScore
を渡すと、同じメモリアドレスへの 2 つの書き込みアクセスを同時に実行しようとするため、競合が発生します。NOTE 演算子は関数であるため、in-out パラメータに長期アクセスすることもできます。例えば、balance(_:_:)
が<^>
という演算子である場合、playerOneScore <^> playerOneScore
と記述すると、balance(&playerOneScore, &playerOneScore)
と同じ競合が発生します。
self
への書き込みアクセス権を持ちます。例えば、各プレイヤーがダメージを受けると減少する活力値と、特殊能力を使用すると減少するエネルギー量を持つゲームを考えてみましょう。restoreHealth()
メソッドでは、self
への書き込みアクセスはメソッドの最初から戻り値を返すまで続きます。この場合、restoreHealth()
内には、Player
インスタンスのプロパティへのアクセスが重複する可能性のある他のコードはありません。下記の shareHealth(with:)
メソッドは、別の Player
インスタンスを in-out パラメータとして受け取り、アクセスが重複する可能性があります。shareHealth(with:)
メソッドを呼び出しても、競合は発生しません。oscar
は mutating メソッドの self
の値のため、メソッド呼び出し中に oscar
への書き込みアクセスがあり、maria
が in-out パラメータとして渡されるため、同じ期間、maria
への書き込みアクセスがあります。次の図に示すように、これらは異なるメモリアドレスにアクセスします。2 つの書き込みアクセスは時間的に重複していますが、競合しません。oscar
を shareHealth(with:)
のパラメータとして渡すと、競合が発生します。self
への書き込みアクセスを必要とし、in-out パラメータは、同じ期間中に teammate
への書き込みアクセスを必要とします。メソッド内では、下の図に示すように、self
と teammate
の両方がメモリ内の同じ位置を参照します。2 つの書き込みアクセスは同じメモリを参照し、それらが重複して競合が発生します。balance(_:_:)
を呼び出すと、playerInformation
への書き込みアクセスが重複しているため、競合が発生します。playerInformation.health
と playerInformation.energy
の両方が in-out パラメータとして渡されます。つまり、balance(_:_:)
は、関数呼び出しの間、それらへの書き込みアクセスを必要とします。どちらの場合も、タプル要素へ書き込みアクセスするには、タプル全体への書き込みアクセスが必要です。これは、同じ期間に playerInformation
への 2 つの書き込みアクセスがあり、競合が発生していることを意味します。holly
がグローバル変数ではなくローカル変数に変更された場合、コンパイラは、構造体の格納されたプロパティへの重複アクセスが安全だと証明できます:balance(_:_:)
への 2 つの in-out パラメータとして渡されます。2 つの格納プロパティは相互作用しないため、コンパイラはメモリの安全性が保たれていることを証明できます。