質問: 環境:Mac/High Sierra 10.13.6/swift4.2/xcode 10.0 ある配列オブジェクトに対して、自分自身や他のいくつかのオブジェクトからの処理要求が同時に起こる可能性がある場合、命令通りに同時に処理が行われますでしょうか。 例えば、配列でappend()をたくさん行う必要がある時に、この処理が行われている途中で他のオブジェクトなどからこの配列を参照するとどのようになりますでしょうか。 処理中のアクセス防止には排他処理を行うしか方法はありませんでしょうか。 排他処理でも良いと思っていますが、他にも方法がある場合知りたいです。
回答: 原則、スレッド間で共有するリソースであって複数個数のデータが入るもの(クラスなり配列なりリストなり)を、マルチスレッドで同時操作する場合には必ず排他制御が必要です。1つのデータであってもアトミックアクセスできないものは排他なり、処理系が提供していればアトミック操作命令が必要です。 例:座標データ `(x, y)` があるとき、排他しないと - スレッド1が `x` を読む - スレッド2が `x` を書く - スレッド2が `y` を書く - スレッド1が `y` を読む と、スレッド1が処理しようとする座標は旧値でも新値でもない壊れた値となります。 例: 32bit CPU で 64bit 変数1つを扱うとき、排他しないと - スレッド1が下位 32bit を読む - スレッド2が下位 32bit を読む - スレッド2が上位 32bit を読む - スレッド2は 64bit 値に対して演算を行う - スレッド2が下位 32bit を書く - スレッド2が上位 32bit を書く - スレッド1が上位 32bit を読む と、やはりスレッド1が読み取った値は壊れた値です。 というわけで1つの変数に対する処理であっても排他が必要となる場合があります。 処理系によっては「アトミック操作」命令を提供してくれているものもあります。そういう場合アトミック操作命令をうまく使うと排他不要になる場合もあります。例: Windows API であれば `InterlockedIncrement` 等。 > > 命令通りに同時に処理が行われますでしょうか。 > > > No! 最近の高速 CPU ではキャッシュメモリやパイプラインの活用のためコア1とコア2で「書き込んだ順番と読み込んだ順番が逆に見えてしまう」ことすらあります。「メモリバリア」なる機能でそういう不具合を回避することができますが実行コストが高いです(とはいえ排他よりは安い)。マルチスレッド構成で最高の実行時性能を追求するにはどういう状況でどういう処理が必要かの深い理解が必要となります。開発コストを下げたいのであれば排他するほうが簡単。