如何使共享值在没有互斥锁的情况下保持一致?

Cre*_*ing 4 c++ multithreading atomic

我有以下代码。我不明白为什么读者会看到不一致的变量值。

uint64_t get_counter() {
    static uint64_t counter = 0;
    static std::mutex m;
    std::unique_lock l(m);
    return ++counter;
}

auto main() -> int {
    // uint64_t shared = 0;
    std::atomic<uint64_t> shared = 0;

    const auto writer = [&shared]() -> void {
        while (true) {
            shared = get_counter();
            std::this_thread::yield();
        }
    };
    const auto reader = [&shared]() -> void {
        while (true) {
            const uint64_t local = shared;
            if (local > shared) {
                cout << local << " " << shared << endl;
            }
            std::this_thread::yield();
        }
    };

    std::thread w1(writer), w2(writer), r(reader);
    r.join();

    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

get_counter只是生成严格递增数字的助手。实际上,它可以被其他更有用的功能所取代。

由于shared永远不应该变小,我期望何时if (local > shared)进行评估,它永远不应该是真的。但是,我得到这样的输出:

1022 1960
642677 644151
645309 645699
1510591 1512122
1592957 1593959
7964226 7965790
8918667 8919962
9094127 9095161
9116800 9117780
9214842 9215720
9539737 9541144
9737821 9739100
10222726 10223912
11197862 11199348
Run Code Online (Sandbox Code Playgroud)

看起来local确实比 小shared,但是为什么输出呢?它是由某些数据竞争引起的吗?如果是这样,如何在不引入互斥锁的情况下解决这个问题?可以std::atomic_thread_fence用来帮忙吗?是否shared必须是std::atomic

Bor*_*der 7

在我看来,以下顺序是可能的:

 Writer1: get_counter() -> 2
 Writer2: get_counter() -> 3
 Writer2: shared = 3
 Reader: local = shared (3)
 Writer1: shared = 2
 Reader: if (local > shared) // 3 > 2
Run Code Online (Sandbox Code Playgroud)

由于您的锁不涵盖生成 + 分配,您在写入共享时存在“竞争条件”,并且它可能从生成中乱序,导致 if 触发。

我将竞争条件放在引号中,因为它不是数据竞争(因为共享是原子的)。