[英]thread safe std::cout using mutex
I try to make some thread safe std::cout
and the best solution for me will look like the following:我尝试制作一些线程安全的
std::cout
,对我来说最好的解决方案如下所示:
void print(std::ostream &out)
{
pthread_mutex_lock(&m_mutex);
std::cout << out;
pthread_mutex_unlock(&m_mutex);
}
I want to use is like this:我想使用的是这样的:
print("hello" << std::endl);
but unfortunately I get a compiler error:但不幸的是我得到一个编译器错误:
test.cpp:38: error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘std::ostream’ {aka ‘std::basic_ostream<char>’})
test.cpp: In function ‘void print(std::ostream&)’:
test.cpp:38:15: error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘std::ostream’ {aka ‘std::basic_ostream<char>’})
as for me this message is absolutely unreadable.对我来说,这条消息是绝对不可读的。
what I do wrong here?我在这里做错了什么?
There are several issues here.这里有几个问题。
First, std::cout << out;
首先,
std::cout << out;
is wrong because there is no matching overload for operator<<
for those operands ( out
is a std::ostream&
here).是错误的,因为对于这些操作数没有匹配的
operator<<
重载(这里out
是一个std::ostream&
)。 This is basically what the so called "unreadable" error message was saying.这基本上就是所谓的“不可读”错误消息所说的。
The same applies with "hello" << std::endl
for the same reason.出于同样的原因,这同样适用于
"hello" << std::endl
。
Moreover, std::endl
is a function and more that than, a templated function. If you ever wanted to pass it as an argument, you will have to specify what overload you need, in this case, std::endl<char, std::char_traits<char>>
.此外,
std::endl
是一个 function ,而且比模板化的 function 还要多。如果您想将它作为参数传递,则必须指定您需要的重载,在本例中, std::endl<char, std::char_traits<char>>
。
You can simplify the notation the following way:您可以通过以下方式简化符号:
auto endl = std::endl<char, std::char_traits<char>>;
This way, you can pass the previoulsy defined endl
function instead.这样,您就可以传递先前定义的
endl
function。
To solve your issue, I think a good solution would be to separate the process in two steps:为了解决您的问题,我认为一个好的解决方案是将过程分为两个步骤:
std::stringstream
here)std::stringstream
) For this purpose, you could hide all the machinery inside a helper class, let's call it Printer
.为此,您可以将所有机器隐藏在助手 class 中,我们称它为
Printer
。
To make it as flexible as you wanted it to be, we can make use of variadic templates.为了让它像你想要的那样灵活,我们可以使用可变参数模板。
The syntax would then be changed from "hello" << value <<...
to "hello", value, ...
.然后语法将从
"hello" << value <<...
更改为"hello", value, ...
。
To sum it up, we can have the definition of the Printer
class as:综上所述,我们可以将
Printer
class 定义为:
class Printer final
{
private:
std::stringstream s;
template <typename T>
void accumulate(T && t)
{
s << std::forward<T>(t);
}
template <typename T, typename ... Ts>
void accumulate(T && t, Ts && ... ts)
{
s << std::forward<T>(t);
accumulate(std::forward<Ts>(ts)...);
}
public:
template <typename ... Ts>
void print(Ts && ... ts)
{
//lock
accumulate(std::forward<Ts>(ts)...);
std::cout << s.view(); // Use s.str() instead if before C++20
s.str(std::string());
s.clear();
//unlock
}
};
Note: If you are before c++20 , you may replace s.view();
注意:如果你在c++20之前,你可以替换
s.view();
with s.str();
与
s.str();
. .
Then you can use it as follows:然后你可以按如下方式使用它:
int main()
{
auto endl = std::endl<char, std::char_traits<char>>;
std::string val("Bye !");
Printer p;
p.print("Hello", " !", '\n', val, endl);
return 0;
}
Output: Output:
Hello !
你好 !
Bye !再见 !
Note: It would be safer to use std::scoped_lock
(or std::lock_guard
if before c++17 ) instead of traditional lock/unlock mechanism since it makes use of RAII to ensure the mutex is released when going out of the scope (in the case an exception is thrown before the release for example).注意:使用
std::scoped_lock
(或std::lock_guard
,如果在c++17之前)而不是传统的锁定/解锁机制会更安全,因为它使用 RAII 来确保在离开 scope(在例如,在发布之前抛出异常的情况)。
Note 2: If you don't want to bother with a Printer
instance, you can declare everything inside as static
so that you could directly use Printer::print(...);
注意 2:如果您不想打扰
Printer
实例,则可以将其中的所有内容声明为static
,这样您就可以直接使用Printer::print(...);
. .
If you want to use the function like如果你想使用 function 之类的
print("hello" );
and within the function you want to output在 function 中你想要 output
std::cout << "hello" << std::endl;
then you need to use the string literal "hello" as the function argument.那么您需要使用字符串文字“hello”作为 function 参数。
For example例如
std::ostream & print( const std::string &s, std::ostream &out = std::cout )
{
pthread_mutex_lock(&m_mutex);
out << s << std::endl;
pthread_mutex_unlock(&m_mutex);
return out;
}
//...
print("hello" );
Or the parameter can be declared as having the type std::string_view
.或者可以将参数声明为具有类型
std::string_view
。 For example例如
std::ostream & print( std::string_view s, std::ostream &out = std::cout )
{
pthread_mutex_lock(&m_mutex);
out << s << std::endl;
pthread_mutex_unlock(&m_mutex);
return out;
}
Ok, a solution that suits me perfectly is using a buffered output as suggested above.好的,一个非常适合我的解决方案是使用上面建议的缓冲 output。
std::stringstream stream;
stream << "value " << some_string << " is " << some_value << std::endl;
std::cout << stream.str();
I've tested this construction with 10 threads that print outs to console quite often.我用 10 个线程测试了这个结构,这些线程经常打印输出到控制台。
You can do it with a maccro.你可以用宏来做到这一点。
#define PRINT(out, message) do { scoped_lock(); out << message << std::endl; } while(false)
Then, when replaced in your code:然后,在您的代码中替换时:
PRINT("hello !");
// actually is equivalent to
{
scoped_lock();
out << "hello !" << std::endl;
}
PRINT("a = " << a << ", b = " << b << std::endl
<< "total = " << compute_total_value());
// actually is equavalent to
{
scoped_lock();
out << "a = " << a << ", b = " << b << std::endl
<< "total = " << compute_total_value() << std::endl;
}
for(int i = 0; i < 10; ++i)
PRINT("i = " << i);
// actually is equavalent to
for(int i = 0; i < 10; ++i)
{
scoped_lock();
out << "i = " << i << std::endl;
}
Note: if you don't want to have the automatic endl added after your message, you can replace it by std::flush
.注意:如果您不想在消息后添加自动 endl,可以将其替换为
std::flush
。
Note: in this kind of code, you need to used a scoped lock instead of lock/unlock instructions, in order to unlock even if an exception was thrown by the stream usage or by the functions called in message
.注意:在这种代码中,您需要使用作用域锁而不是锁定/解锁指令,以便即使 stream 用法或
message
中调用的函数抛出异常也能解锁。
Note: do {} while(false) is used in the maccro, because it's the only scope that can be considered as a unique instruction by the compiler.注意: do {} while(false) 在宏中使用,因为它是唯一可以被编译器视为唯一指令的 scope。 It avoid insidious bugs when used in a loop without braces.
当在没有大括号的循环中使用时,它避免了潜在的错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.