繁体   English   中英

线程安全std :: cout

[英]Thread safe std::cout

以下程序仍将输出交织到std::cout 我试图添加一个std::mutex来控制通过std::lock_guardstd::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};
}

我是否需要通过单独的类或专用线程来控制访问?

您的问题在这里: std::lock_guard<std::mutex>{global_mtx}; 创建一个锁卫并立即释放它。 您需要创建一个变量来保存锁,例如std::lock_guard<std::mutex> lock{global_mtx};

防止忘记命名锁的一种方法是制作一个可用作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};
}

您创建四个Timer对象,每个对象都有自己的唯一互斥对象。 因此,当t2运行其线程时,它将锁定自己的互斥锁,并且可以这样做是因为t1在开始其循环之前已锁定了另一个互斥锁。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM