2015-02-06 23 views
0

我在調試一些線程代碼,並且遇到了一些我不理解的行爲。爲什麼只有一個鎖和一個原子計數器會錯誤地喚醒條件變量?

我創建了一個線程向量。我有變量atomic_uintCounteratomic_boolStop,告訴線程什麼時候應該停止。每個線程線程在計數器不爲零的情況下等待,然後遞減它。

在主線程中,我增加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(除了主線程)。

回答

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; 

(你不需要調用​​那裏)

相關問題