易失性和多线程:以下是线程安全的吗?

spo*_*ang 22 c++ multithreading volatile

假设有运行两个线程Thread1(),并Thread2()分别.线程1只设置一个全局标志来告诉线程2退出,线程2定期检查它是否应该退出.

volatile bool is_terminate = false;

void Thread1()
{
    is_terminate = true;
}

void Thread2()
{
    while (!is_terminate) {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

我想问上面的代码是否安全,假设访问is_terminate是原子的.我已经知道许多材料状态,volatile一般不能确保线程安全.但是在只共享一个原子变量的情况下,我们真的需要使用锁来保护共享变量吗?

jal*_*alf 17

可能有点线程安全.

线程安全往往取决于上下文.如果你从未读过它,更新bool 总是线程安全的.如果您确实从中读取,那么答案取决于您何时从中读取,以及读取的含义.

在某些CPU上,但不是全部,对类型对象的写入bool将是原子的.x86 CPU通常会使其成为原子,但其他人可能不会.如果更新不是原子的,那么添加volatile对您没有帮助.

但下一个问题是重新排序.编译器(和CPU)将按volatile指定的顺序执行对变量的读/写操作,而不进行任何重新排序.这很好.

但它无法保证volatile相对于所有非内存访问重新排序一个内存访问volatile.所以一个常见的例子是你定义某种标志来保护对资源的访问,你创建标志volatile,然后编译器移动资源访问,以便检查标志之前发生.允许这样做,因为它没有重新排序两个volatile访问的内部排序,而只是一个volatile和一个非访问volatile.

老实说,我问的问题是为什么不正确呢?有可能volatile在这种情况下工作,但为什么不为自己省去麻烦,并使它更清楚,它是正确的?相反,在它周围拍打一个记忆障碍.

  • 我的意图是在这种简单的情况下锁定对共享变量的访问是不值得的。 (2认同)

Bo *_*son 11

它不是线程安全的.

例如,如果线程在具有单独高速缓存的CPU上运行,则没有语言规则表明在编写volatile变量时要同步高速缓存.如果有的话,另一个线程可能在很长一段时间内都看不到变化.


以另一种方式回答:

如果volatile足够线程安全,为什么C++ 0x用原子操作添加整章?

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2047.html

  • 不完全正确.CPU本身始终同步缓存.一个CPU的缓存中没有值,没有其他内核可以看到它. (4认同)
  • @skwllsp:仅运行代码并不能保证任何结果。它也适用于*下一个*编译器版本吗?如果明天再运行的话还能用吗?CPU 或编译器提供什么保证?当您考虑线程安全时,这才是最重要的 (2认同)