22 June 2023

C++ 核心指南目录

“Don’t try to use volatile for synchronization”

理由

与其他语言不同,在 C++ 中, volatile 不提供原子性操作能力。不能在线程之间同步,不能防止编译器或硬件对指令重排序引起的问题。简单说,就是 volatile 不提供任何并行运算支持。

坏例子

int free_slots = max_slots; // current source of memory for objects

Pool* use()
{
    if (int n = free_slots--) return &pool[n];
}

这里,我们遇到一个问题:在单线程环境下,这段代码没有任何问题。但是如果多个线程执行这段代码,就会在 free_slots 这个变量上出现数据竞争情况。两个线程可能从 free_slots 得到一样的数值。所以,熟悉其他编程语言的人,可能会通过以下方法解决这个问题:

volatile int free_slots = max_slots; // current source of memory for objects

Pool* use()
{
    if (int n = free_slots--) return &pool[n];
}

然而,这样做并不能达到同步的效果:数据竞争仍然存在!

C++ 提供的机制是 atomic 类型:

atomic<int> free_slots = max_slots; // current source of memory for objects

Pool* use()
{
    if (int n = free_slots--) return &pool[n];
}

现在,这个操作是原子操作,而非分步骤的:读取-自减-写入,不然在操作过程中,其他线程可能在其中插入进行操作。

其他情况

在复杂的例子中可以使用 mutex