В кодовой базе, которую я рассмотрел, я нашел следующую идиому.
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 будет работать в двух разных процессорах - эта схема может выйти из строя".
"Но это сказано в книге..."
"Тихо!", он быстро замолчал: "Возможно, теоретически это не гарантировано, но на практике тот факт, что вы использовали вызов функции, фактически является барьером памяти. Компилятор не будет изменять порядок инструкций global.data = data
, поскольку он не может знать, будет ли кто-либо использовать его в вызове функции, а архитектура x86 обеспечит, чтобы другие процессоры увидели эту часть глобальных данных к тому времени, когда поток B считывает команду из канала. Будьте уверены, у нас достаточно реальных мировые проблемы, о которых нужно беспокоиться. Нам не нужно вкладывать дополнительные усилия в фиктивные теоретические проблемы.
"Будьте уверены, мой мальчик, со временем вы поймете, чтобы отделить реальную проблему от проблем, связанных с I-need-to-get-a-PhD".
Правильно ли он? Это действительно не проблема на практике (скажем, x86, x64 и ARM)?
Это против всего, что я узнал, но у него есть длинная борода и действительно умный вид!
Дополнительные баллы, если вы можете показать мне часть кода, доказывающую, что он ошибается!