我需要在我的日志记录应用程序中锁定标准输出,以防止在记录到标准输出的多线程应用程序中出现字符串交错。无法弄清楚如何使用移动构造函数或 std::move 或其他方式将 unique_lock 移动到另一个对象。
我创建了用于设置配置和封装的对象,并想出了如何使用静态 std::mutex 锁定 stdout 以锁定这些对象(称为分片)。
这样的东西对我有用:
l->log(1, "Test message 1");
虽然这很好并且可以使用模板和可变数量的参数来实现,但我想接近更多类似流的可能性。我正在寻找这样的东西:
*l << "Module id: " << 42 << "value: " << 42 << std::endl;
我不想强制用户使用连接和 to_string(42) 预先计算字符串,我只是想找到一种方法来锁定标准输出。
到目前为止,我的方法是创建运算符 << 和另一个对象锁定流,正如其他答案中所建议的那样。事情是我不知道如何将互斥体移动到另一个对象。我的代码:
locked_stream& shard::operator<<(int num)
{
static std::mutex _out_mutex;
std::unique_lock<std::mutex> lock(_out_mutex);
//std::lock_guard<std::mutex> lock (_out_mutex);
std::cout << std::to_string(num) << "(s)";
locked_stream s;
return s;
}
在将输入输出到 std::cout 之后,我想将锁移动到对象流中。
最佳答案
在这种情况下,我会小心不要在函数中使用静态锁,因为您会为您创建的每个流运算符获得不同的锁。
您需要的是在流创建时锁定一些“输出锁”,并在流销毁时解锁。如果您只是包装 std::ostream,则可以利用现有的流操作。这是一个有效的实现:
#include <mutex>
#include <iostream>
class locked_stream
{
static std::mutex s_out_mutex;
std::unique_lock<std::mutex> lock_;
std::ostream* stream_; // can't make this reference so we can move
public:
locked_stream(std::ostream& stream)
: lock_(s_out_mutex)
, stream_(&stream)
{ }
locked_stream(locked_stream&& other)
: lock_(std::move(other.lock_))
, stream_(other.stream_)
{
other.stream_ = nullptr;
}
friend locked_stream&& operator << (locked_stream&& s, std::ostream& (*arg)(std::ostream&))
{
(*s.stream_) << arg;
return std::move(s);
}
template <typename Arg>
friend locked_stream&& operator << (locked_stream&& s, Arg&& arg)
{
(*s.stream_) << std::forward<Arg>(arg);
return std::move(s);
}
};
std::mutex locked_stream::s_out_mutex{};
locked_stream locked_cout()
{
return locked_stream(std::cout);
}
int main (int argc, char * argv[])
{
locked_cout() << "hello world: " << 1 << 3.14 << std::endl;
return 0;
}
在 ideone 上:https://ideone.com/HezJBD
另外,请原谅,由于在线编辑器的笨拙,上面会混合使用空格和制表符。
关于multithreading - C++ 使用互斥量锁定流运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36439373/