如何创建一个新类来继承 ostream 并将其用作 cout 但带有锁定

How can I create a new class to inherit ostream and use it as cout but with lock

本文关键字:cout 锁定 继承 创建 何创建 一个 新类 ostream      更新时间:2023-10-16

我使用 std::cout 在控制台上打印日志。由于该程序是多线程的,如果我在cout之后使用多个"<<"操作,打印结果将是无序的。


例如,如果一个线程执行cout<< "A" << "B" << endl;则另一个线程可能在 A 和 B 之间执行cout << "C";。结果将是"ACB"。


因此,我将编写一个新类来继承ostream(实际上是basic_ostream<char, char_traits<char>>(,并在初始化 cout 时添加一个锁,因此打印输出应遵循正确的顺序。

一种选择是创建一个类,该类保存对流的引用,但在其整个生命周期中保持锁。下面是一个简单的示例:

#include <iostream>
#include <mutex>
struct LockedOstream {
    std::lock_guard<std::mutex> lg_;
    std::ostream& os_;
    LockedOstream(std::mutex& m, std::ostream& os)
      : lg_{m}
      , os_{os}
    { }
    std::ostream& stream() const { return os_; }
};
int main()
{
    std::mutex m;
    LockedOstream(m, std::cout).stream() << "foo " << "barn";
    //                        ^ locked now                   ^ unlocked now
}

只要构成单个输出"单位"的所有打印都发生在同一语句中,这就可以工作。


编辑:实际上,继承版本比我最初预期的要好得多:

#include <iostream>
#include <mutex>
class LockedOstream : public std::ostream {
    static std::mutex& getCoutMutex()
        // use a Meyers' singleton for the cout mutex to keep this header-only
    {
        static std::mutex m;
        return m;
    }
    std::lock_guard<std::mutex> lg_;
  public:
    // Generic constructor
    // You need to pass the right mutex to match the stream you want
    LockedOstream(std::mutex& m, std::ostream& os)
      : std::ostream(os.rdbuf())
      , lg_{m}
    { }
    // Cout specific constructor
    // Uses a mutex singleton specific for cout
    LockedOstream()
      : LockedOstream(getCoutMutex(), std::cout)
    { }
};
int main()
{
    LockedOstream() << "foo " << "barn";
    //            ^ locked now          ^ unlocked now
}

顺便说一句:

  • using namespace std;被广泛认为是不良做法,并且
  • 我不是std::endl的忠实粉丝也

(虽然后者有时会引起争议,但至少知道并做出明智的选择是个好主意(。

您可以定义自己的函数

template<typename... Ts>
void locked_print(std::ostream& stream, Ts&&... ts)
{
    static std::mutex mtx;
    std::lock_guard<std::mutex> guard(mtx);
    (stream << ... << std::forward<Ts>(ts));
}

当您想确保它是独家的时,您可以像locked_print(std::cout, 1, 2, "bar");

由于outstream << x1 << x2 << ...多个函数调用,因此除了锁定所有内容直到销毁相同的外流之外,您无法在内部执行此操作。调用约束时,只需强制约束:

{
  std::lock_guard<std::mutex> guard(global_mutex);
  // your print here
}