2015-12-03 49 views
0

我有一個從一個線程遞增的計數器。在主線程中,我基本上是通過調用一個類的數據成員來打印出來的。在下面的代碼中,沒有任何內容正在打印出來。從兩個線程訪問計數器

#include <iostream> 
#include <thread> 
#include <windows.h> 
#include <mutex> 

std::mutex mut; 

class Foo 
{ 
public: 
    Foo(const int& m) : m_delay(m), m_count(0) 
    {} 

    void update() 
    { 
     std::cout << "count: " << this->m_count << std::endl; 
    } 

    void operator()() 
    { 
     while (true){ 
      mut.lock(); 
      m_count++; 
      mut.unlock(); 

      Sleep(m_delay); 
     } 
    } 

private: 
    int m_delay; 
    int m_count; 
}; 

Foo *obj = new Foo(200); 

int main() 
{ 
    std::thread *t = new std::thread(*obj); 
    t->join(); 

    while(true) 
    { 
     obj->update(); 

     Sleep(10); 
    } 

    return 0; 
} 
+3

是您的問題:「爲什麼沒有任何被打印出來?」或者這篇文章是更多的聲明? :) –

+0

不需要在這個代碼中使用用戶鎖.... – Netwave

+3

@CroCo你什麼時候期待't-> join()'返回? – Biffen

回答

3

與原代碼的問題是,這種複製Foo對象:

std::thread *t = new std::thread(*obj); 

這意味着,增量發生在副本,所以在原來Foo值不會改變,而因此當main將其打印出來(如果移動錯位join()),該值始終相同。

一個解決方案是使用一個參考而不是一個拷貝:

std::thread *t = new std::thread(std::ref(*obj)); 

您還需要保護變量的互斥鎖的讀取(或使用std::atomic<int>的計數器),以避免因同時未定義行爲讀寫一個非原子變量。

您也應該直接停止使用mut.lock()mut.unlock(),而不是使用use a scoped lock

也沒有必要不必要地在堆上創建事物,過度使用new是先學習Java和C#的人的壞習慣。

您還可以通過用標準C++替換特定於Windows的Sleep調用來使代碼具有可移植性。

正確的版本是:

#include <iostream> 
#include <thread> 
#include <chrono> 
#include <mutex> 

std::mutex mut; 

class Foo 
{ 
public: 
    Foo(std::chrono::milliseconds m) : m_delay(m), m_count(0) 
    {} 

    void update() 
    { 
     int count = 0; 
     { 
      std::lock_guard<std::mutex> lock(mut); 
      count = m_count; 
     } 
     std::cout << "count: " << count << std::endl; 
    } 

    void operator()() 
    { 
     while (true) 
     { 
      { 
       std::lock_guard<std::mutex> lock(mut); 
       m_count++; 
      } 

      std::this_thread::sleep_for(m_delay); 
     } 
    } 

private: 
    std::chrono::milliseconds m_delay; 
    int m_count; 
}; 

Foo obj(std::chrono::milliseconds(200)); 

int main() 
{ 
    std::thread t(std::ref(obj)); 
    while(true) 
    { 
     obj.update(); 

     std::this_thread::sleep_for(std::chrono::milliseconds(10)); 
    } 
    t.join(); 
    return 0; 
} 

另外,使用原子變量,因此你不需要互斥:

#include <iostream> 
#include <thread> 
#include <chrono> 
#include <atomic> 

class Foo 
{ 
public: 
    Foo(std::chrono::milliseconds m) : m_delay(m), m_count(0) 
    {} 

    void update() 
    { 
     std::cout << "count: " << m_count << std::endl; 
    } 

    void operator()() 
    { 
     while (true) 
     { 
      m_count++; 

      std::this_thread::sleep_for(m_delay); 
     } 
    } 

private: 
    std::chrono::milliseconds m_delay; 
    std::atomic<int> m_count; 
}; 

Foo obj(std::chrono::milliseconds(200)); 

int main() 
{ 
    std::thread t(std::ref(obj)); 
    while(true) 
    { 
     obj.update(); 

     std::this_thread::sleep_for(std::chrono::milliseconds(10)); 
    } 
    t.join(); 
    return 0; 
} 
+0

爲什麼你在while循環里加了{}'? – CroCo

+1

在睡覺之前釋放互斥鎖。 'lock_guard'的構造函數獲取鎖,並且析構函數釋放它。否則,鎖在休眠時將被保持,而另一個線程將無法鎖定它來讀取該值。我添加了另一個使用原子變量的版本,所以不需要互斥體。 –