2016-12-14 66 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