C++ 在多个线程中使用 std::cout

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18277304/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:50:38  来源:igfitidea点击:

using std::cout in multiple threads

c++multithreadingc++11mutexstdthread

提问by LxL

I write a simple program for testing Thread in c++11 but std::coutdoesnt work as I expect.

我写了一个简单的程序来测试 c++11 中的线程,但std::cout没有按我预期的那样工作。

class Printer
{
public:
    void exec()
    {
        mutex m;
        m.lock();
        cout<<"Hello  "<<this_thread::get_id()<<endl;
        chrono::milliseconds duration( 100 );
        this_thread::sleep_for( duration );
        m.unlock();

    }
};

int main()
{
    Printer printer;

    thread firstThread([&printer](){
        while(1)
            printer.exec();

    });
    thread secondThread([&printer](){
        while(1)
            printer.exec();
    });

    firstThread.join();
    secondThread.join();     
}

some of the results :

一些结果:

Hello 11376
Hello 16076
Hello 16076
Hello Hello 11376
16076
Hello 11376
,....

I used mutex for locking threads so I cant understand why two threads are executing std::coutat the same time. It seams very weired to me.Can any one explain what is happening!?!

我使用互斥锁来锁定线程,所以我无法理解为什么两个线程同时执行std::cout。我觉得很奇怪。谁能解释一下发生了什么!?!

回答by hmjd

The threads are using differentmutexinstances as the mutexis a local variable in the exec()function so locking the mutexis pointless as each thread will be locking its own mutexresulting in no synchronization between the threads. The same mutexinstance must be used by the threads to achieve synchronization.

线程使用不同的mutex实例,因为mutexexec()函数中的局部变量,因此锁定mutex是没有意义的,因为每个线程都将锁定自己的,mutex导致线程之间没有同步。mutex线程必须使用相同的实例来实现同步。

To correct in the posted code, make the mutexa member variable. However, if another Printerobject was created then there would be no synchronization between threads that used different Printerinstances. In this case, the mutexwould need to be a staticmember variable to ensure synchronization:

要在发布的代码中进行更正,请创建mutex成员变量。但是,如果Printer创建了另一个对象,则使用不同Printer实例的线程之间将不存在同步。在这种情况下,mutex需要是一个static成员变量以确保同步:

class Printer
{
public:
    //...
private:
    static std::mutex mtx_;
};

std::mutex Printer::mtx_;


To ensure a mutexis always released, regardless if a function exits normally or via an exception, use std:lock_guard:

为了确保 amutex始终被释放,无论函数是正常退出还是通过异常退出,请使用std:lock_guard

std::lock_guard<std::mutex> lock(m); // 'm' locked, and will be
                                     // unlocked when 'lock' is destroyed.
std::cout<< "Hello  " << std::this_thread::get_id() << std::endl;
std::chrono::milliseconds duration( 100 );
std::this_thread::sleep_for( duration );

回答by Howard Hinnant

The accepted answer is correct. However it is nice to separate concerns:

接受的答案是正确的。但是,将关注点分开是很好的:

  1. You need a way to print to std::coutin a thread safe manner.
  2. You need to create objects/functors/functions to run in threads and launch them.
  1. 您需要一种以std::cout线程安全的方式打印的方法。
  2. 您需要创建对象/函子/函数以在线程中运行并启动它们。

Here is a utility I use that just concentrates on collecting arguments to std::coutand streaming them out under a static std::mutex:

这是我使用的一个实用程序,它只专注于收集参数std::cout并在 a 下将它们流式传输static std::mutex

#include <iostream>
#include <mutex>

std::ostream&
print_one(std::ostream& os)
{
    return os;
}

template <class A0, class ...Args>
std::ostream&
print_one(std::ostream& os, const A0& a0, const Args& ...args)
{
    os << a0;
    return print_one(os, args...);
}

template <class ...Args>
std::ostream&
print(std::ostream& os, const Args& ...args)
{
    return print_one(os, args...);
}

std::mutex&
get_cout_mutex()
{
    static std::mutex m;
    return m;
}

template <class ...Args>
std::ostream&
print(const Args& ...args)
{
    std::lock_guard<std::mutex> _(get_cout_mutex());
    return print(std::cout, args...);
}

This code can be reused for streams other than std::cout, but the above is specialized to just target std::cout. With this your Printer::exec()can now be significantly simplified:

这段代码可以重用于流以外的流std::cout,但上面的代码专门用于 target std::cout。有了这个,您Printer::exec()现在可以显着简化:

void exec()
{
    print("Hello ", std::this_thread::get_id(), '\n');
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

Now, not only will your Printeruse coutin a threadsafe manner, and has been simplified (e.g. doesn't need to maintain its own mutexfor cout), but all of your other types and functions can also use coutand all interoperate together safely. The printfunction itself now maintains the mutex, and that fact is encapsulated away from all of print's clients.

现在,您的Printer使用不仅会cout以线程安全的方式进行,而且已经得到简化(例如,不需要维护自己的mutexfor cout),而且您所有的其他类型和函数也可以cout安全地使用并一起进行互操作。该print函数本身现在维护mutex,这实际上是从所有的封装远print的客户。

回答by Conchylicultor

I'm sharing the trick from Nicolás given in this questionthat I find to be more elegant than Howard Hinnant implementation. The idea is to create a temporary ostringstream object and put the protection code on the destructor.

我正在分享 Nicolás 在这个问题中给出的技巧,我发现它比 Howard Hinnant 的实现更优雅。这个想法是创建一个临时的 ostringstream 对象并将保护代码放在析构函数上。

/** Thread safe cout class
  * Exemple of use:
  *    PrintThread{} << "Hello world!" << std::endl;
  */
class PrintThread: public std::ostringstream
{
public:
    PrintThread() = default;

    ~PrintThread()
    {
        std::lock_guard<std::mutex> guard(_mutexPrint);
        std::cout << this->str();
    }

private:
    static std::mutex _mutexPrint;
};

std::mutex PrintThread::_mutexPrint{};

You can then use it as a regular std::cout, from any thread:

然后,您可以std::cout从任何线程将其用作常规:

PrintThread{} << "val = " << 33 << std::endl;

The object collect data as a regular std::ostringstream. As soon the coma is reached, the object is destroyed and flush all collected information.

该对象定期收集数据std::ostringstream。一旦达到昏迷,对象就会被销毁并刷新所有收集的信息。

回答by Walter

You may consider a global std::mutex cout_mutex;(somewhere in your namespaces), which is used to protected std::coutoutput. Make sure you use std::lock<std::mutex>(so you cannot forget to unlock the mutex and for exception safety).

您可以考虑使用全局std::mutex cout_mutex;(在您的命名空间中的某处),用于保护std::cout输出。确保你使用std::lock<std::mutex>(所以你不能忘记解锁互斥锁和异常安全)。