Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;11:是什么阻止商店提升超过锁的起始点';什么是临界截面?_C++_C++11_Concurrency_Language Lawyer_Memory Model - Fatal编程技术网

C++ C++;11:是什么阻止商店提升超过锁的起始点';什么是临界截面?

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

我的理解是,一个自旋锁可以用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);
  }

 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
的执行