私がレビューしたコードベースで、次のイディオムを見つけました。
void notify(struct actor_t act) {
write(act.pipe, "M", 1);
}
// thread A sending data to thread B
void send(byte *data) {
global.data = data;
notify(threadB);
}
// in thread B event loop
read(this.sock, &cmd, 1);
switch (cmd) {
case 'M': use_data(global.data);break;
...
}
「保持してください」と、私のチームのシニアメンバーである作者に言いました。「ここにはメモリバリアはありませんglobal.data
。キャッシュからメインメモリにフラッシュされる保証はありません。スレッドAとスレッドBが実行される場合2つの異なるプロセッサ-このスキームは失敗する可能性があります。」
シニアプログラマーはニヤリと笑い、靴ひもを結ぶ方法を5歳の男の子に説明するかのようにゆっくりと説明しました。彼の長いあごひげを引っ掻くために一時停止しました、「しかし、私たちはこのイディオムにバグがあったことはありません」。
「しかし、それは本の中で述べています...」
「静かに!」と彼はすぐに私を黙らせました。「理論的には保証されないかもしれませんが、実際には、関数呼び出しを使用したという事実は事実上メモリバリアです。コンパイラは命令global.data = data
を並べ替えません。関数呼び出しでそれを使用する人は誰でも、x86アーキテクチャは、スレッドBがパイプからコマンドを読み取るまでに、他のCPUがこのグローバルデータを確実に認識できるようにします。実際の問題は十分にありますので、ご安心ください。偽の理論上の問題に余分な労力を費やす必要はありません。
「私の少年は安心してください。やがて、本当の問題を博士号を取得する必要のある非問題から分離することが理解できるでしょう。」
彼は正しいですか?それは実際には問題ではありませんか(x86、x64、ARMなど)?
それは私が学んだすべてに反します、しかし彼は長いあごひげと本当にスマートな外見を持っています!
彼が間違っていることを証明するコードを見せてくれれば、追加のポイントがあります!