C++ C++;11:是什么阻止商店提升超过锁的起始点';什么是临界截面?
我的理解是,一个自旋锁可以用C++11原子实现 一个acquire CAS on lock和一个release store on unlock,如下所示:C++ C++;11:是什么阻止商店提升超过锁的起始点';什么是临界截面?,c++,c++11,concurrency,language-lawyer,memory-model,C++,C++11,Concurrency,Language Lawyer,Memory Model,我的理解是,一个自旋锁可以用C++11原子实现 一个acquire CAS on lock和一个release store on unlock,如下所示: class SpinLock { public: void Lock() { while (l_.test_and_set(std::memory_order_acquire)); } void Unlock() { l_.clear(std::memory_order_release); } priv
class SpinLock {
public:
void Lock() {
while (l_.test_and_set(std::memory_order_acquire));
}
void Unlock() {
l_.clear(std::memory_order_release);
}
private:
std::atomic_flag l_ = ATOMIC_FLAG_INIT;
};
考虑在获取锁然后执行盲写的函数中使用它
到某个共享位置:
int g_some_int_;
void BlindWrite(int val) {
static SpinLock lock_;
lock_.Lock();
g_some_int_ = val;
lock_.Unlock();
}
我感兴趣的是如何限制编译器将其翻译为
生成的汇编代码
我理解为什么写入g\u some\u int\u
无法迁移到任务结束后
编译器输出中的关键部分——这意味着
不保证被获取锁的下一个线程看到,即
由发布/获取订单保证
但是,是什么阻止编译器在
锁旗?我认为编译器可以对不同的数据进行重新排序
生成程序集代码时的内存位置。有什么特别规定吗
防止写入在前面的原子存储之前被重新排序
它是按程序顺序排列的吗
我在这里寻找语言律师的答案,最好是涵盖std::atomic
以及std::atomic_标志
编辑以包含评论中可能提出问题的内容
更清楚。问题的核心是:标准的哪一部分说
抽象机器在写入之前必须观察l\ucode>是否为false
g_some_int
我怀疑答案是“写的东西不可能高高在上”
“无限循环”或“写入不能高于原子写入”。也许是
甚至“你错了,写的东西永远都可以重新排序”。但我在找
用于标准中的特定引用。假设您有两个使用自旋锁的函数:
SpinLock sl;
int global_int=0;
int read(){
sl.Lock();
int res=global_int;
sl.Unlock();
return res;
}
void write(int val){
sl.Lock();
global_int=val;
sl.Unlock();
}
如果对BlindWrite
的两个调用同时发生在不同的线程上,那么其中一个(称为A)将获取锁;另一个(B)将在Lock
的循环中旋转
然后,A向g_some_int
,并调用Unlock
,其中包含对clear
的调用,这是一个商店发布。写入在调用clear
之前排序,因为它位于同一线程中
B然后在Lock
中唤醒,这一次test_和_set
调用返回false
。这是一个读取调用clear
存储的值的负载获取,因此对clear
的调用与对测试和设置的调用同步
测试和设置调用锁定是一个负载获取,在盲写中写入g\u一些int
之前进行排序,因为它在同一个线程中
由于第一个写入线程A在调用clear
之前排序,这与线程B中调用test_和_set
同步,而线程B在写入线程B之前排序,因此写入线程A发生在写入线程B之前
如果编译器在调用Lock
之前将写入提升到g_some_int
,那么从线程B进行的写入可能发生在写入线程A之前。这将违反在排序之前发生的规则,因此是不允许的
一般来说,这意味着编译器不能将任何东西提升到加载获取之上,因为它可能违反了在排序之前发生的规则。锁上的内存\u顺序\u获取
和解锁上的内存\u顺序\u释放
。请参见内存\u顺序
。我也不明白为什么编译器在解锁
后不能移动赋值。在您的示例中,一个有效的、无种族歧视的程序无论如何都无法观察到g\u some\u int\u
的值,除非有一些额外的外部同步。@RaymondChen:请再次阅读我的问题,搜索“这是由发布/获取顺序保证的”。我能理解为什么你所说的对一种重新排序是正确的,但对另一种是错误的。内存模型是一个C++级别的概念。它没有说任何关于“生成的汇编代码”的内容,只说程序的可观察行为。编译器可以自由地生成任何程序集,这些程序集导致生成的程序的可观察行为与标准指定的抽象机器的可能执行之一相匹配。例如,一个能够进行全程序优化的编译器可能会注意到,你从来没有读过g_some init\u
,并将整个BlindWrite
函数优化为no-op。如果没有从g_some int\u
读取,那么重新排序是不可见的,因此这个问题是没有意义的。编译器可以完全优化g\u some\u int\u
变量。我假设在代码的其他地方有一个受保护的读取g\u some\u int\u
。这就是我的理解,如果我错了请纠正我:Sequenced before基本上适用于在同一线程上执行的所有操作。编译器被允许首先对这些指令进行重新排序的原因是,只要遵守“仿佛”规则,他就可以在排序之前违反sequenced。在单线程上下文中,这意味着在对相同数据进行写入之前不重新排序读取。在多线程环境中,它还意味着考虑原子和锁约束的同步。我在《标准》中找不到一句话专门说抽象机器在其副作用方面必须遵守“先发生后发生”的关系,但似乎很自然地认为这是隐含的。非常感谢。1.9/13给定任何两个评估A
和B
,如果A
在B
之前排序,则A
的执行应先于B
的执行