C ++ std :: atomic变量的线程同步问题

时间:2019-05-14 11:52:35

标签: c++11 atomic

以下程序偶尔打印一次“坏”输出时,给我意外的行为。这两个线程应该使用两个std :: atomic变量's_lock1'和's_lock2'进行同步。在func2中,为了将's_var'变量设置为1,它必须在's_lock2'中原子地存储一个非零值,并且另一个线程(func1)尚未更新's_lock1'变量。但是,在func1中以某种方式打印了意外的“错误”输出。 s_lock2.load()语句似乎返回false。该代码段是否存在问题?这与内存排序有关吗?

我正在安装了Centos 7的8核Linux服务器上运行它。任何帮助将不胜感激。

#include <iostream>
#include <thread>
#include <atomic>
#include <unistd.h>

std::atomic_uint s_lock1 = 0;
std::atomic_uint s_lock2 = 0;
std::atomic_uint s_var = 0;

static void func1()
{
    while (true) {
        s_lock1.store(1, std::memory_order_release);
        if (s_lock2.load(std::memory_order_acquire) != 0) {
            s_lock1.store(0, std::memory_order_release);
            continue;
        }
        if (s_var.load(std::memory_order_acquire) > 0) {
            printf("bad\n");
        }
        usleep(1000);
        s_lock1.store(0, std::memory_order_release);
    }
}

static void func2()
{
    while (true) {
        s_lock2.store(1, std::memory_order_release);
        if (s_lock1.load(std::memory_order_acquire) != 0) {
            s_lock2.store(0, std::memory_order_release);
            continue;
        }
        s_var.store(1, std::memory_order_release);
        usleep(5000);
        s_var.store(0, std::memory_order_release);
        s_lock2.store(0, std::memory_order_release);
    }
}

int main()
{
    std::thread t1(func1);
    std::thread t2(func2);
    t1.join();
    t2.join();
}

1 个答案:

答案 0 :(得分:3)

由于Intel CPU中的存储缓冲区,此锁定算法可能会中断:这些存储区不会直接进入1级缓存,而是会在存储缓冲区中排队一段时间,因此在此期间对另一个CPU不可见:

  

为使指令执行的性能最佳化,IA-32架构允许脱离Pentium 4,Intel Xeon和P6系列处理器中称为处理器排序的强排序模型。 这些处理器排序变体(在这里称为内存排序模型)可以提高性能,例如允许读取先于缓冲写入。这些变体中的任何一个的目的都是为了提高指令执行速度,甚至在多处理器系统中也可以保持内存一致性。

需要使用std::memory_order_seq_cst刷新存储缓冲区以使此锁定起作用,以便将存储锁定(例如,加载和存储的默认内存顺序,您可以执行s_lock1 = 1;)。 std::memory_order_seq_cst用于存储导致​​编译器在存储之后生成xchg指令或插入mfence指令,这两者都使存储的效果对其他CPU可见:

  

标记为memory_order_seq_cst的原子操作不仅以与释放/获取顺序相同的方式对内存进行排序(所有发生的事情-在一个线程中进行存储之前,都会在执行加载的线程中看到明显的副作用),而且< strong>还为所有如此标记的原子操作建立单个总修改顺序。对于多个生产者-多个消费者的情况,所有消费者必须观察所有生产者以相同顺序发生的行为,可能需要顺序排序。总顺序排序需要在所有多核系统上使用完整的存储器篱笆CPU指令。这可能会成为性能瓶颈,因为它会迫使受影响的内存访问传播到每个内核。

工作示例:

std::atomic<unsigned> s_lock1{0};
std::atomic<unsigned> s_lock2{0};
std::atomic<unsigned> s_var{0};

void func1() {
    while(true) {
        s_lock1.store(1, std::memory_order_seq_cst);
        if(s_lock2.load(std::memory_order_seq_cst) != 0) {
            s_lock1.store(0, std::memory_order_seq_cst);
            continue;
        }
        if(s_var.load(std::memory_order_relaxed) > 0) {
            printf("bad\n");
        }
        usleep(1000);
        s_lock1.store(0, std::memory_order_seq_cst);
    }
}

void func2() {
    while(true) {
        s_lock2.store(1, std::memory_order_seq_cst);
        if(s_lock1.load(std::memory_order_seq_cst) != 0) {
            s_lock2.store(0, std::memory_order_seq_cst);
            continue;
        }
        s_var.store(1, std::memory_order_relaxed);
        usleep(5000);
        s_var.store(0, std::memory_order_relaxed);
        s_lock2.store(0, std::memory_order_seq_cst);
    }
}

int main() {
    std::thread t1(func1);
    std::thread t2(func2);
    t1.join();
    t2.join();
}