为什么只有一个锁和一个原子计数器的条件变量会错误地唤醒?

Why does the condition variable wake up erroneously with only one lock and an atomic counter?

我正在调试一些线程代码,遇到了一些我不理解的行为。

我创建了一个线程向量。我有变量 atomic_uint Counteratomic_bool Stop,告诉线程何时应该停止。每个线程线程等待计数器不为零的条件,然后递减它。

在主线程中,我递增 Counter,并根据条件调用 notify_one()。代码如下

#include <thread>
#include <condition_variable>
#include <atomic>
#include <vector>
#include <iostream>
#include <cstdlib>
#include <mutex>

int main() {
    const std::size_t       Tasks = 100u;
    const std::size_t       Repetitions = 100u;
    const std::size_t       Threads = 4u;
    std::mutex              Mutex;
    std::condition_variable Condition;
    std::atomic_uint        Counter(0);
    std::atomic_uint        MainCounter(0);
    std::atomic_uint        ThreadCounter(0);
    std::atomic_bool        Stop( false );


    std::vector<std::thread> v;
    for ( std::size_t i = 0; i < Threads; i++ ) {
        v.emplace_back( [&ThreadCounter,&Mutex, &Condition, &Counter, &Stop]() -> void {
            while ( true ) {
                {
                    std::unique_lock<std::mutex> lock( Mutex );
                    Condition.wait( lock, [&Counter, &Stop]() -> bool {
                        //wait while this is false
                        return Counter.load() >= 0u || Stop;
                    } );

                    if ( Stop && Counter == 0u ) {
                        return;
                    }
                    ThreadCounter++;
                    if ( Counter == 0 ) {
                        continue;
                    }
                    Counter--;
                }
            }   //while
        });
    }   //for

    for ( std::size_t i = 0u; i < Tasks; i++ ) {
        MainCounter++;
        Counter++;
        Condition.notify_one();
    }
    while ( Counter != 0u ) {
        std::this_thread::yield();
    }

    Stop = true;
    Condition.notify_all();
    for ( auto& t: v ) {
        t.join();
    }

    std::cout << "ThreadCounter = " << ThreadCounter.load() << std::endl;
    std::cout << "MainCounter = " << MainCounter.load() << std::endl;    

    return 0;
}

在线程代码中,我有一个额外的原子,ThreadCounter,用于跟踪 Counter 实际递减了多少次。

ThreadCounter 的递增次数总是比 Condition.notify_one() 的调用次数多:

ThreadCounter = 212
MainCounter = 100

当我用一把锁锁定条件时,这是怎么发生的?据我了解,一次只有一个线程可以访问Counter(除了主线程)。

Each thread thread waits on a condition that the counter is not zero

这实际上不是你的情况:

Condition.wait( lock, [&Counter, &Stop]() -> bool {
    //wait while this is false
    return Counter.load() >= 0u || Stop;
        // ^^^^^^^^^^^^^^^^^^^^
} );

Counter 是无符号的,因此 >= 0u 始终为真。如果 Counter == 0 那么你的循环体将增加 ThreadCounter 可能很多次,因此你的差异。

你的意思可能是:

return Counter > 0 || Stop;

(你不需要在那里调用 .load()