据我所知,对变量的并发访问需要某种同步(互斥、原子、内存屏障......),否则无论尝试多少次,一个线程中的读取都可能永远不会获得更新的值。
但是,我的同事说 MESI 协议(不考虑没有 MESI 或类似东西的 cpu)能够在 cpu 缓存之间自动同步,如果读取由其他线程更新的变量而在读写中没有任何同步(只是普通读取,例如“if(a != 0)”),经过一段时间后,如果继续尝试,read 将最终获得更新的值。我认为这里不能保证。
所以我写了一个代码来测试这个:
volatile int * volatile a = 0; // avoid compiler reorder
void set() {
a = new int(1);
std::cout << "set complete" << std::endl;
}
void read(int i) {
while(1) {
if(a != 0) {
std::cout << i << " detected" << std::endl;
break;
}
}
}
int main()
{
std::thread td00(std::bind(read, 0));
std::thread td01(std::bind(read, 1));
std::thread td02(std::bind(read, 2));
std::thread td03(std::bind(read, 3));
std::thread td04(std::bind(read, 4));
// wait a moment to make sure 'set' gets called after 'read' runs
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::thread td1(set);
td1.join();
td00.detach();
td01.detach();
td02.detach();
td03.detach();
td04.detach();
std::this_thread::sleep_for(std::chrono::minutes(60));
return 0;
}
但是,运行会受到许多因素的影响,有时会阻塞,有时会打印“检测”。不能作为有力的证据。
我已经搜索过了,但文档对此并不清楚。看起来 MESI 确实可以做“自动同步”(程序员不需要做任何事情),'PrRd' 和 'PrWr' 似乎只是正常的读写请求,没有 LOCK 或 CMPXCHG 或类似的东西。但是为了加快速度,它引入了一个存储缓冲区,这会使cpu混乱,并使“自动同步”的效果失效。为了解决这个问题,程序员需要使用工具(内存屏障)来控制它。这意味着程序员必须手动进行同步才能使事情正确。
我理解这个正确吗?如果是,假设程序员不手动执行,是否可以保证获取更新值的时间延迟?我认为读取可能永远不会获得更新的值,但是我找不到证据。