2016-12-14 64 views
1

下面的程序仍然交錯輸出到std::cout。我試圖add a std::mutex to control accessstd::cout通過std::lock_guard,但它still interleaves螺紋安全std :: cout

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

std::mutex global_mtx{}; 

class Timer { 
public: 
    Timer(size_t time, const std::function<void(void)>& f) : time{std::chrono::milliseconds{time}}, f{f} {} 
    ~Timer() { wait_thread.join(); } 

private: 
    void wait_then_call() 
    { 
     std::unique_lock<std::mutex> lck{mtx}; 
     for(int i{10}; i > 0; --i) { 
      { 
       std::lock_guard<std::mutex>{global_mtx}; 
       std::cout << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl; 

      } 
      cv.wait_for(lck, time/10); 
     } 
     f(); 
    } 
    std::mutex mtx; 
    std::condition_variable cv{}; 
    std::chrono::milliseconds time; 
    std::function <void(void)> f; 
    std::thread wait_thread{[this]() {wait_then_call(); }}; 
}; 

int main() 
{ 
    auto f = []() {std::lock_guard<std::mutex>{global_mtx}; std::cout << "---------------- I waited to print! ----------------" << std::endl; }; 
    Timer t1{3'000,f}; 
    Timer t2{6'000,f}; 
    Timer t3{2'000,f}; 
    Timer t4{1'000,f}; 
} 

我是否需要通過單獨的類或專用線程來控制訪問?

回答

3

你的問題是在這裏:std::lock_guard<std::mutex>{global_mtx};創建一個鎖警衛並立即釋放它。您需要創建一個變量來鎖定鎖,如std::lock_guard<std::mutex> lock{global_mtx};

+0

啊哈! [TIL](http://stackoverflow.com/a/2298796/1460794)。 – wally

0

您創建四個Timer對象,每個對象都有其自己的唯一互斥對象。所以當t2運行它的線程時,它會鎖定自己的互斥鎖,因爲t1在開始其循環之前鎖定了一個不同的互斥鎖。防止忘記命名鎖

3

的一種方法是使你可以作爲一個IO操作器使用的鎖定對象:

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

std::mutex global_mtx{}; 

struct lockio 
{ 
    lockio(std::mutex& m) : lock_(m) {} 

    std::unique_lock<std::mutex> lock_; 
}; 
std::ostream& operator<<(std::ostream& os, const lockio&) { 
    return os; 
} 

class Timer { 
public: 
    Timer(size_t time, const std::function<void(void)>& f) : time{std::chrono::milliseconds{time}}, f{f} {} 
    ~Timer() { wait_thread.join(); } 

private: 
    void wait_then_call() 
    { 
     std::unique_lock<std::mutex> lck{mtx}; 
     for(int i{10}; i > 0; --i) { 
      { 
       std::cout << lockio(global_mtx) << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl; 

      } 
      cv.wait_for(lck, time/10); 
     } 
     f(); 
    } 
    std::mutex mtx; 
    std::condition_variable cv{}; 
    std::chrono::milliseconds time; 
    std::function <void(void)> f; 
    std::thread wait_thread{[this]() {wait_then_call(); }}; 
}; 

int main() 
{ 
    auto f = []() { std::cout << lockio(global_mtx) << "---------------- I waited to print! ----------------" << std::endl; }; 
    Timer t1{3'000,f}; 
    Timer t2{6'000,f}; 
    Timer t3{2'000,f}; 
    Timer t4{1'000,f}; 
} 

另一個(可能更好)的方法是創建一個小幫手模板功能包裹保護的操作:

#include <iostream> 
#include <thread> 
#include <condition_variable> 

std::mutex global_mtx{}; 

template<class Mutex, class F> 
decltype(auto) with_lock(Mutex &m, F &&f) { 
    std::lock_guard<Mutex> lock(m); 
    return f(); 
}; 

class Timer { 
public: 
    Timer(size_t time, const std::function<void(void)> &f) : time{std::chrono::milliseconds{time}}, f{f} {} 

    ~Timer() { wait_thread.join(); } 

private: 
    void wait_then_call() { 
     std::unique_lock<std::mutex> lck{mtx}; 
     for (int i{10}; i > 0; --i) { 
      with_lock(global_mtx, [&] { 
       std::cout << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl; 
      }); 
      cv.wait_for(lck, time/10); 
     } 
     f(); 
    } 

    std::mutex mtx; 
    std::condition_variable cv{}; 
    std::chrono::milliseconds time; 
    std::function<void(void)> f; 
    std::thread wait_thread{[this]() { wait_then_call(); }}; 
}; 

int main() { 
    auto f = []() { 
     with_lock(global_mtx, [] 
     { 
      std::cout << "---------------- I waited to print! ----------------" << std::endl; 
     }); 
    }; 
    Timer t1{3'000, f}; 
    Timer t2{6'000, f}; 
    Timer t3{2'000, f}; 
    Timer t4{1'000, f}; 
} 

另一種方式:

#include <iostream> 
#include <thread> 
#include <condition_variable> 


struct locked { 

    std::ostream& cout() const { return std::cout; } 
    std::ostream& cerr() const { return std::cerr; } 

private: 
    static std::mutex& mutex() { 
     static std::mutex stdio_mutex; 
     return stdio_mutex; 
    } 
    std::unique_lock<std::mutex> lock_{mutex()}; 
}; 

class Timer { 
public: 
    Timer(size_t time, const std::function<void(void)> &f) : time{std::chrono::milliseconds{time}}, f{f} {} 

    ~Timer() { wait_thread.join(); } 

private: 
    void wait_then_call() { 
     std::unique_lock<std::mutex> lck{mtx}; 
     for (int i{10}; i > 0; --i) { 
      locked().cout() << "Thread " << wait_thread.get_id() << " countdown at: " << '\t' << i << std::endl; 
      cv.wait_for(lck, time/10); 
     } 
     f(); 
    } 

    std::mutex mtx; 
    std::condition_variable cv{}; 
    std::chrono::milliseconds time; 
    std::function<void(void)> f; 
    std::thread wait_thread{[this]() { wait_then_call(); }}; 
}; 

int main() { 
    auto f = []() { 
     locked().cout() << "---------------- I waited to print! ----------------" << std::endl; 
    }; 
    Timer t1{3'000, f}; 
    Timer t2{6'000, f}; 
    Timer t3{2'000, f}; 
    Timer t4{1'000, f}; 
} 
+0

兩者都很有用。 'lockio'對於這個問題似乎更加清晰,並且[作品](http://coliru.stacked-crooked.com/a/aab44acae4b30d2b)對於我來說,互斥體是一個靜態成員變量。第二個模板選項當然可以用於任何功能。 – wally

+0

第三個清楚發生了什麼事情。我喜歡它用來在'struct'中定義靜態互斥體的魔法。 – wally