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

标签 c++ multithreading c++11 thread-safety

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

我创建了一个线程 vector 。我有变量 atomic_uint Counter 和一个 atomic_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())

关于c++ - 为什么只有一个锁和一个原子计数器的条件变量会错误地唤醒?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28365534/

相关文章:

c# - 当我需要抓取网站时,我应该使用 BackgroundWorker 还是 Threads?

java - 无法创建新的 native 线程

c++ - C++11 中分布的多态性

c++ - 错误 : no type named ‘_traits’ in ‘class date::year_month_day’ ?

c++ - 使用单个定界符拆分字符串

c++ - 绘制 ArcCtg ( x ) 图

java - C++ 中的等效标识符

c++ - 如何保留 'first' 和 'second' 用 struct 替换 std::pair ?

java - java 对象中的等待池表示

c++ - 来自模板参数的字符串的 constexpr 长度