使用互斥体的线程安全 std::cout

发布于 2025-01-18 04:40:19 字数 938 浏览 4 评论 0 原文

我尝试使一些线程安全 std::cout ,对我来说最好的解决方案如下所示:

void print(std::ostream &out) 
{
    pthread_mutex_lock(&m_mutex);
    std::cout << out;
    pthread_mutex_unlock(&m_mutex);
}

我想使用是这样的:

print("hello" << std::endl);

但不幸的是我收到编译器错误:

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>’})
   

对我来说这是消息绝对不可读。

我在这里做错了什么?

I try to make some thread safe std::cout and the best solution for me will look like the following:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

橙幽之幻 2025-01-25 04:40:19

这里有几个问题。

首先,std::cout << out; 是错误的,因为这些操作数的 operator<< 没有匹配的重载(out 是一个 std::ostream&代码>此处)。这基本上就是所谓的“不可读”错误消息所说的内容。

这同样适用于 "hello" << std::endl 出于同样的原因。

此外, std::endl 是一个函数 不仅仅是一个模板化函数。如果您想将其作为参数传递,则必须指定所需的重载,在本例中为 std::endl>

您可以通过以下方式简化符号:

auto endl = std::endl<char, std::char_traits<char>>;

这样,您可以传递先前定义的 endl 函数。


为了解决您的问题,我认为一个好的解决方案是将过程分为两个步骤:

  • 将数据累积到流中(我将在此处使用 std::stringstream )
  • 打印累积的数据。

为此,您可以将所有机制隐藏在辅助类中,我们将其称为 Printer

为了使其像您希望的那样灵活,我们可以使用可变参数模板。
然后语法将从 "hello" <<值<< ...“你好”,值,...

综上所述,我们可以将 Printer 类的定义为:

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
        }
};

注意: 如果您之前是 ,您可以替换s.view();< /代码> 与s.str();

然后您可以按如下方式使用它:

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;
}

输出:

你好!
再见!

这里是实例


注意: 这样做会更安全使用 std::scoped_lock (或 std::lock_guard 如果在 ) 而不是传统的锁定/解锁机制,因为它利用 RAII 来确保互斥体在超出范围时被释放(例如,在释放之前抛出异常的情况)。

注2:如果你不想使用Printer实例,你可以将里面的所有内容声明为static,这样你就可以直接使用打印机::print(...);

There are several issues here.

First, std::cout << out; is wrong because there is no matching overload for operator<< for those operands (out is a std::ostream& here). This is basically what the so called "unreadable" error message was saying.

The same applies with "hello" << std::endl for the same reason.

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>>.

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.


To solve your issue, I think a good solution would be to separate the process in two steps:

  • Accumulate your data into a stream (I will use std::stringstream here)
  • Print the accumulated data.

For this purpose, you could hide all the machinery inside a helper class, let's call it 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, ....

To sum it up, we can have the definition of the Printer class as:

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 , you may replace s.view(); with 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:

Hello !
Bye !

Live example here


Note: It would be safer to use std::scoped_lock (or std::lock_guard if before ) 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).

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(...);.

安人多梦 2025-01-25 04:40:19

使用类似于 and 的函数

print("hello" );

如果您想在要输出的函数中

std::cout << "hello" << std::endl;

,那么您需要使用字符串文字“hello”作为函数参数。

例如

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" );

,或者可以将参数声明为类型 std::string_view。例如

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;
}

If you want to use the function like

print("hello" );

and within the function you want to output

std::cout << "hello" << std::endl;

then you need to use the string literal "hello" as the function argument.

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. 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;
}
盗梦空间 2025-01-25 04:40:19

好吧,一个完全适合我的解决方案是使用上面建议的缓冲输出。

std::stringstream stream;
stream << "value " << some_string << " is " << some_value << std::endl;
std::cout << stream.str();

我已经用 10 个线程测试了这个结构,这些线程经常打印到控制台。

Ok, a solution that suits me perfectly is using a buffered output as suggested above.

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.

幻梦 2025-01-25 04:40:19

您可以使用MacCro进行。

#define PRINT(out, message) do { scoped_lock(); out << message << std::endl; } while(false)

然后,当在代码中替换时:

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;
}

注意:如果您不想在消息后添加自动端口,则可以通过 std :: flush 替换它。

注意:在这种代码中,您需要使用示波器锁定而不是锁定/解锁说明,即使流动使用或通过<<<代码>消息。

注意: do {} whe(false)在MacCro中使用,因为它是编译器可以将其视为唯一的范围。当在没有牙套的循环中使用时,它会避免隐藏的错误。

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.

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.

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. It avoid insidious bugs when used in a loop without braces.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文