マルチスレッド時のメモリのシンクロ保証イロイロ

PROCESS WARPでは、今マルチスレッドプログラムの分散処理機能を作っています。同機能の実現には複数デバイスでメモリを同調、シンクロさせる必要があります。PROCESS WARPはMap Reduceや関数型言語でない普通のプログラムを実行するVMを作ろうとしているため、この点が複雑になります。 メモリのシンクロのタイミングは分散処理のナイーブな部分です。 メモリのシンクロ部分の設計にあたり、C/C++のメモリのシンクロがどのようになっているか調べました。

スレッド間で授受する値はatomic(原子)操作を行う

以下の様な単一の命令であっても、実はaの値は不定のタイミングが存在する可能性があり、C/C++はソレを許容しています。

a ++;
// 次の処理〜

普通のシングルスレッドのプログラムの場合は次の処理が行われるまでに変数aの値が確定するので問題になりませんが、マルチスレッドの場合、変更途中のaの値を取得すると変更前の値とも変更後の値とも異なる値が取得される場合があります。 そんな事にならないように安全に値を書き換える、値を取得するのがatomic操作です。

たとえ話

  • ホワイトボードに数字を書く係(A)の人がいます
  • ソレを見て記録する係(B)の人がいます
  • 一人で両方の仕事をしている場合は、問題は発生しません(シングルスレッド)
  • 二人で仕事を手分けした場合、Aが10とホワイトボードに書いている途中にBが1と記録するかも知れませんし、Aが右から01と書くかもしれません
  • なので、Aが最後にピリオドを打つまでを1つの作業とし、それを確認した段階でBが記録を行うようにしました(atomic)

d.hatena.ne.jp

d.hatena.ne.jp

d.hatena.ne.jp

スレッド間で値の操作に順序性を持たせる場合はメモリバリア操作を行う

C/C++コンパイラやCPUの命令デコーダシングルスレッドで問題にならない範囲で最適化のために命令実行順序を変更します。変数書き換えがアトミックだとしても以下のコードを実行した場合、aとb、CPUがどちらを先に書き換えるかは分かりません。条件分岐など、変数を利用する処理が実行される前までに書き換えられます。変数cに至っては条件分岐に利用されないため、if文実行時の値が0のままの可能性もあります。

// a = 0; b = 0; c = 0;
a = 1;
b = 1;
c = 1;
if (a < b) // 条件分岐

値の書き換え順序を、「ここまでには変数aを書き換えておく」「変数bの書き換えはここ以降で行う」のように保証するのがメモリバリアです。

yamasa.hatenablog.jp

スレッド間で同時実行数の制御を行うにはmutex(ミューテクス or ミューテックス)、semaphore(セマフォ)を使う

メモリバリアは数値の書き換えに限定していましたが、一連の処理(クリティカルセクション)を同時に実行しないよう保証する機能があります。 セマフォは、処理を同時にn個のスレッドしかできないようにするという仕組みです。 特に、同時に1つのスレッドしか処理しない場合をミューテクスと呼びます。

セマフォ