如何创建一个新的 class 来继承 ostream 并将其用作 cout 但带有锁
How can I create a new class to inherit ostream and use it as cout but with lock
我使用 std::cout 在控制台上打印日志。由于程序是多线程的,如果我在cout后使用多个"<<"操作,打印结果会很乱。
例如,如果一个线程执行 cout<< "A" << "B" << endl;
,另一个线程可能会在 A 和 B 之间执行 cout << "C";
。结果将是 "ACB"。
所以我要写一个新的class来继承ostream
(其实就是basic_ostream<char, char_traits<char>>
)并且在cout初始化的时候加锁,所以打印out 应该遵循正确的顺序。
由于 outstream << x1 << x2 << ...
是 多个 函数调用,除了锁定所有内容直到破坏同一个外流之外,您无法在内部执行此操作。
你可以在调用它时强制你的约束:
{
std::lock_guard<std::mutex> guard(global_mutex);
// your print here
}
一个选择是创建一个 class 来保存对流的引用,但在其整个生命周期中都持有锁。这是一个简单的例子:
#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 " << "bar\n";
// ^ locked now ^ unlocked now
}
只要形成单个 "unit" 输出的所有打印都发生在同一语句中,这就有效。
编辑:其实继承版比我原先想象的要好很多:
#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 " << "bar\n";
// ^ locked now ^ unlocked now
}
顺便说一句:
using namespace std;
is widely considered bad practice,以及
- 我不太喜欢
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");
我使用 std::cout 在控制台上打印日志。由于程序是多线程的,如果我在cout后使用多个"<<"操作,打印结果会很乱。
例如,如果一个线程执行 cout<< "A" << "B" << endl;
,另一个线程可能会在 A 和 B 之间执行 cout << "C";
。结果将是 "ACB"。
所以我要写一个新的class来继承ostream
(其实就是basic_ostream<char, char_traits<char>>
)并且在cout初始化的时候加锁,所以打印out 应该遵循正确的顺序。
由于 outstream << x1 << x2 << ...
是 多个 函数调用,除了锁定所有内容直到破坏同一个外流之外,您无法在内部执行此操作。
你可以在调用它时强制你的约束:
{
std::lock_guard<std::mutex> guard(global_mutex);
// your print here
}
一个选择是创建一个 class 来保存对流的引用,但在其整个生命周期中都持有锁。这是一个简单的例子:
#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 " << "bar\n";
// ^ locked now ^ unlocked now
}
只要形成单个 "unit" 输出的所有打印都发生在同一语句中,这就有效。
编辑:其实继承版比我原先想象的要好很多:
#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 " << "bar\n";
// ^ locked now ^ unlocked now
}
顺便说一句:
using namespace std;
is widely considered bad practice,以及- 我不太喜欢
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");