据我所知,对变量的并发访问需要某种类型的同步(互斥、原子、内存屏障……)否则,无论尝试多少次,一个线程中的读取可能永远不会获得更新值。
然而,我的同事说,MESI协议(不考虑cpu没有MESI或类似的东西)能够在cpu缓存之间自动同步,如果读取一个由其他线程更新的变量,并且在读写时没有任何同步(只是简单地读,例如" if (a != 0)"),经过一段时间,读将最终获得更新值,如果它继续尝试。我认为这里没有保证。
所以我写了一个代码来测试这个:
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”看起来只是正常的读写请求,没有锁或CMPXCHG或类似的东西。然而,为了提高速度,它引入了一个存储缓冲区,这会使cpu混乱,使“自动同步”的效果失效。为了修复这种混乱,程序员需要使用工具(内存屏障)来控制它。这意味着程序员必须手动进行同步才能使事情变得正确。
我的理解正确吗?如果是,假设程序员不手动操作,是否有时间延迟的保证来获得更新值?我认为一个读取器可能永远不会得到更新的值,但是我找不到证据。
发布于 2021-05-12 18:13:19
结论是: x86_64是缓存一致性的,普通的简单写入对于共享一条总线的所有其他核心或cpus是全局可见的。
然而,这对于编写普通应用程序代码(不包括诸如编译器、操作系统内核等低级内容)是无用的。语言内存模型对编码器完全隐藏了这些缓存一致性协议。编码不应该依赖或利用那些协议功能,因为编译器或语言虚拟机、运行时会扰乱、优化你的代码。即使你知道具体会发生什么,违反语言内存模型来编写代码仍然是微妙和容易出错的。
其中一种可能性是,即使在设置函数调用之前,也会使示例代码打印"x detect“(显示如何发生的reference ),或者,变量a存储在寄存器中,如果没有volatile关键字,则使mesi无能为力。更不用说大多数语言没有c/c++比较易失性关键字,这允许程序员只选择编译器而不是“改变”源代码。
https://stackoverflow.com/questions/67482612
复制相似问题