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

时间:2015-02-06 12:10:59

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

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

我创建了一个线程向量。我有变量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(除主线程外)。

1 个答案:

答案 0 :(得分:3)

  

每个线程线程在计数器不为零的条件下等待

这实际上不是你的条件:

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()