C++ 如何在std::atomic<;上实现一个简单的自旋锁;T>;这样编译器就不会';你不能优化它吗?

C++ 如何在std::atomic<;上实现一个简单的自旋锁;T>;这样编译器就不会';你不能优化它吗?,c++,multithreading,c++11,optimization,C++,Multithreading,C++11,Optimization,我一直在尝试使用标准C++11/14来锁定线程的非常简单的方法,现在我使用的是这样的东西 std::atomic<bool> setup_ready(false); { // thread 1 while (!setup_ready.load()) std::this_thread::yield(); // do something } { // thread 2 // perform some setup setup_ready.store(true)

我一直在尝试使用标准C++11/14来锁定线程的非常简单的方法,现在我使用的是这样的东西

std::atomic<bool> setup_ready(false);

{
  // thread 1
  while (!setup_ready.load()) std::this_thread::yield();

  // do something
}

{
  // thread 2
  // perform some setup
  setup_ready.store(true);
}

我提出的唯一解决方案是在混合中添加一个可变变量,但我不确定这是否是最好的方法。我还试着检查原子学的标准,但我找不到关于编译器优化的详细信息。

编译器不允许优化出
这个线程::yield()
,除非它能确定它没有副作用——它不能,因为它没有副作用


原子加载操作不能被优化掉,因为它包含一个内存屏障,并且被专门定义为拾取其他线程所做的修改。

编译器不允许优化出
这个线程::yield()
,除非它能确定它没有副作用——它不能,因为它没有副作用


原子加载操作不能被优化掉,因为它包含一个内存屏障,并且是专门定义来获取其他线程所做的修改的。

大多数编译器优化都不是标准明确允许的;它们在很大程度上依赖于实现。我非常怀疑,如果去掉了成品率,任何编译器都不会优化你的循环。我知道gcc和cl肯定不会。编译器知道原子可以“自发地”更改,这要归功于其他线程中的写入,因此它无法优化循环。不确定“依次执行整个循环”是什么意思-循环包含2个调用(
setup\u ready.load()
yield
),即使这些调用中的一个被优化了,也并不意味着另一个会被优化。顺便说一句:(我曾经看到过一个关于这个的问题,但现在找不到它)用
yield
实现自旋锁被认为是不好的。其基本原理是,
收益率
可能会在相当长的一段时间内消失;但是自旋锁应该只在锁中的代码非常快的情况下使用(当然不会进行任何系统调用)。注意,如果没有
yield
的旋转会影响性能,那么首先这里不是一个拥有旋转锁的好地方,最好是等待一个同步对象。@M实际上我的第一个实现没有包括yield,但是通过添加它,我能够获得10倍的性能提升。最后我还是切换到了一个条件变量,但我明白你的意思;它们在很大程度上依赖于实现。我非常怀疑,如果去掉了成品率,任何编译器都不会优化你的循环。我知道gcc和cl肯定不会。编译器知道原子可以“自发地”更改,这要归功于其他线程中的写入,因此它无法优化循环。不确定“依次执行整个循环”是什么意思-循环包含2个调用(
setup\u ready.load()
yield
),即使这些调用中的一个被优化了,也并不意味着另一个会被优化。顺便说一句:(我曾经看到过一个关于这个的问题,但现在找不到它)用
yield
实现自旋锁被认为是不好的。其基本原理是,
收益率
可能会在相当长的一段时间内消失;但是自旋锁应该只在锁中的代码非常快的情况下使用(当然不会进行任何系统调用)。注意,如果没有
yield
的旋转会影响性能,那么首先这里不是一个拥有旋转锁的好地方,最好是等待一个同步对象。@M实际上我的第一个实现没有包括yield,但是通过添加它,我能够获得10倍的性能提升。最后,我还是切换到了一个条件变量,但是,是的,我明白你的意思。谢谢你这么做,尽管从我所读的内容来看,
这个线程::yield()
是实现定义的,因为它主要是一个提示,它可以是一个NOP,这反过来又不会产生副作用,并且可以优化。至少这是我从阅读标准中得到的。如果它是一个NOP,那么它是否被优化也没关系。它仍然必须运行循环来进行原子加载,仅仅因为函数是NOP并不意味着编译器可以对其进行优化。只有当函数定义对编译器可见,这通常意味着它是内联的,或者是在进行跨前程优化(并且C或C++运行时不太可能提供必要的信息来启用IPO)。大多数编译器不会完全删除调用?还是我误解了你的意思?我就是这么说的。如果编译器不知道函数的功能,它如何删除对函数的调用?(它不一定必须是内联的,但函数体必须在编译的翻译单元中可见,这仅适用于在同一翻译单元中定义的函数,内联或非内联)。感谢这一点,尽管从我所读的内容来看,
this_thread::yield()
是实现定义的,因为它主要是一个提示,它可能是一种NOP,反过来又不会产生副作用,并且可以优化。至少这是我从阅读标准中得到的。如果它是一个NOP,那么它是否被优化也没关系。它仍然必须运行循环来进行原子加载,仅仅因为函数是NOP并不意味着编译器可以对其进行优化。只有当函数定义对编译器可见,这通常意味着它是内联的,或者是在进行跨前程优化(并且C或C++运行时不太可能提供必要的信息来启用IPO)。大多数编译器不会删除
{
  // thread 1
  while (!setup_ready.load()) ;

  // do something
}