2016-12-14 11 views
1

次のプログラムは、出力をまだstd::coutにインターリーブします。私はadd a std::mutex to control accessからstd::coutまでを経由しましたが、それは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

それぞれ独自のミューテックスオブジェクトを持つ4つの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'はこの問題のためにもっときれいに思えますし、静的メンバー変数としてmutexを使って私のためにも(http://coliru.stacked-crooked.com/a/aab44acae4b30d2b)働いています。もちろん、2番目のテンプレートオプションは、どのような関数でも動作するという利点があります。 – wally

+0

3つ目は、何が起こっているのかを明確にします。私はそれが 'struct'のstatic mutexを定義するのに使う魔法が好きです。 – wally

関連する問題