简体   繁体   English

同步STD cout输出多线程

[英]Synchronizing STD cout output multi-thread

Latelly I've been working with multi-thread coding, after a while writing I realized that if I used std::cout in different boost::threads, the output would came without a logical order, the program that I'm testing is something like: 后来我一直在使用多线程编码,在写了一段时间之后,我意识到如果我在不同的boost :: threads中使用std :: cout,输出将没有逻辑顺序,我正在测试的程序是就像是:

#include <boost/thread/thread.hpp>
#include <iostream>

int my01( void )
{
    std::cout << "my01" << std::endl;
    return 0;
}
/* my02, my03 and my04 are the same with different outputs*/
[...]
int main( void )
{
    boost::thread t1(&my01);
    boost::thread t2(&my02);
    boost::thread t3(&my03);
    boost::thread t4(&my04);

    while(!t1.joinable() || !t2.joinable() || !t3.joinable() || !t4.joinable());

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    std::cout << "The end!" << std::endl;
    getchar();
    return 0;
}


And the output is usually like (it changes): 输出通常是(更改):

my02my01 my02my01
my04 my04
my03 my03
BLANK LINE 空行
The end! 结束!

With this issue in mind I was thinking of creating a single thread to manage all of the outputs, so they would be in order like: 考虑到这个问题,我正在考虑创建一个线程来管理所有输出,因此它们的顺序如下:

my01 my01
my02 my02
my03 my03
my04 my04
The end! 结束!

Which is the optimal way to write such thread or to manage those outputs? 编写此类线程或管理这些输出的最佳方法是哪种?
Please read the answers to this question too: Is cout synchronized/thread-safe? 请也阅读此问题的答案: cout是否同步/线程安全?

Ps:I'm using Visual C++ 2010 Express and my cpu has 8 different cores. ps:我使用的是Visual C ++ 2010 Express,我的cpu有8个不同的内核。

Thank you for your time! 感谢您的时间!

First of all, you might consider avoiding all the explicit thread management, and instead use std::async to launch your tasks in some arbitrary number of separate threads. 首先,您可以考虑避免所有显式的线程管理,而是使用std::async在任意数量的独立线程中启动任务。

Second, instead of doing the I/O in the threads themselves, you want to create results, and do the output itself serially. 其次,您不想创建线程本身中的I / O,而是要创建结果,并依次进行输出。 This means the thread function just creates some data, and leaves it to the caller to actually write that out: 这意味着线程函数仅创建一些数据,并将其留给调用者以将其实际写出:

std::string process(int value) {
     std::ostringstream buffer;
     buffer << "my" << std::setfill('0') << std::setw(2) << value;
     return buffer.str();
}

Then we need to launch four copies of that asychronously: 然后,我们需要异步启动四个副本:

std::vector<std::future<std::string> > results;

for (int i=0; i<4; i++)
    results.push_back(std::async(std::launch::async, process, i));

Then we get the results and print them out in order: 然后我们得到结果并按顺序打印出来:

for (auto &r : results)
    std::cout << r.get() << "\n";

Putting those together, we could get code like this: 将它们放在一起,我们可以获得如下代码:

#include <string>
#include <iostream>
#include <thread>
#include <future>
#include <sstream>
#include <vector>
#include <iomanip>

std::string process(int value) {
     std::ostringstream buffer;
     buffer << "my" << std::setfill('0') << std::setw(2) << value;
     return buffer.str();
}

int main() { 
    std::vector<std::future<std::string>> rets;

    for (int i=0; i<4; i++)
        rets.push_back(std::async(std::launch::async, process, i));

    for (auto & t : rets) {
        t.wait();
        std::cout << t.get() << "\n";
    }
}

I should add one minor point: I'm basing this on standard C++11 future s. 我要补充一点:我将其基于标准C ++ 11 future I believe the basic idea should also work with Boost future s (upon which the standard was based) but I haven't tested that. 我相信基本想法也应该与Boost future一起使用(基于该标准),但我尚未对其进行测试。 I'd expect that some minor adjustments (eg, to names) will be needed to work with Boost's futures. 我希望对Boost的期货进行一些小的调整(例如,对名称进行调整)。

I resolved it by coding up a thin wrapper that locks a mutex on starting writing to the stream and releases it, along with flushing the stream, once the write statement is completed. 我通过编写一个瘦包装器来解决它,该包装器在开始写入流时锁定互斥锁,并在写入语句完成后将其释放,并冲洗流。

Usage: replace std::cout by safe_cout. 用法:将std :: cout替换为safe_cout。

Keep in mind it does not support fancy std::cout features like std::endl. 请记住,它不支持像std :: endl这样的std :: cout功能。

See the code below or grab it from here: https://github.com/dkorolev/felicity/blob/master/safe_ostream.h 请参阅下面的代码或从此处获取: https : //github.com/dkorolev/felicity/blob/master/safe_ostream.h

#include <cassert>
#include <iostream>
#include <mutex>
#include <memory>

struct safe_ostream {
  struct guarded_impl {
    guarded_impl() = delete;
    guarded_impl(const guarded_impl&) = delete;
    void operator=(const guarded_impl&) = delete;
    guarded_impl(std::ostream& ostream, std::mutex& mutex) : ostream_(ostream), guard_(mutex) {
    }
    ~guarded_impl() {
      ostream_.flush();
    }
    template<typename T> void write(const T& x) {
      ostream_ << x;
    }
    std::ostream& ostream_;
    std::lock_guard<std::mutex> guard_;
  };
  struct impl {
    impl() = delete;
    void operator=(const impl&) = delete;
    impl(std::ostream& ostream, std::mutex& mutex) : unique_impl_(new guarded_impl(ostream, mutex)) {
    }
    impl(const impl& rhs) {
      assert(rhs.unique_impl_.get());
      unique_impl_.swap(rhs.unique_impl_);
    }
    template<typename T> impl& operator<<(const T& x) {
      guarded_impl* p = unique_impl_.get();
      assert(p);
      p->write(x);
      return *this;
    }
    mutable std::unique_ptr<guarded_impl> unique_impl_;
  };
  explicit safe_ostream(std::ostream& ostream) : ostream_(ostream) {
  }
  template<typename T> impl operator<<(const T& x) {
    return impl(ostream_, mutex_) << x;
  }
  std::ostream& ostream_;
  std::mutex mutex_;
};
safe_ostream safe_cout(std::cout);
safe_ostream safe_cerr(std::cerr);

You either need to impose an order on the threads so that the ordering of the output is as you want, (perhaps by passing thread-instances or events to the appropriate threads so that they can only execute in your order), or you could give all the outputs a thread-sequence number, queue all the outputs to one 'print' thread and, in there, keep a list of any out-of-order lines so that the printout is as you want. 您可能需要在线程上强加一个顺序,以便输出的顺序是您想要的(也许通过将线程实例或事件传递给适当的线程,以便它们只能按您的顺序执行),或者您可以给所有输出都具有一个线程序列号,将所有输出排队到一个“打印”线程中,并在该线程中保留所有乱序行的列表,以便根据需要进行打印。

In the case of a 'real' app, (ie. not a trivial test app that misuses threads), where the threads do a lot of work in parallel on sequential buffers whose order must be preserved, forcing threads to wait for each other is not usually a reasonable option. 在“真实”应用程序的情况下(即不是滥用线程的琐碎的测试应用程序),其中线程在必须保留顺序的顺序缓冲区上并行执行大量工作,从而迫使线程彼此等待通常不是一个合理的选择。 It's usual to use sequence numbers and reassemble the buffer-stream afterwards. 通常使用序列号,然后重新组合缓冲流。

Give each thread a std::ostringstream to write output to. 给每个线程一个std::ostringstream以写入输出。 At the end of the program, print each thread's output in order. 在程序末尾,按顺序打印每个线程的输出。

How else would you do it, considering that thread 4 may finish long before thread 1? 考虑到线程4可能比线程1早完成,您将如何做?

Use locking. 使用锁定。 If you can use boost, do eg 如果可以使用升压,请执行例如

int my01(boost::mutex *coutGuard)
{
  {
     // lock cout until the closing brace
     boost::mutex::scoped_lock lock(*coutGuard);

     std::cout << "my01" << std::endl;
  }

  return 0;
}

int main( void )
{
   boost::mutex coutGuard;

   boost::thread t1(boost::bind(&my01, &coutGuard));
   ...
}

Instead of a scoped_lock , lock_guard may be used. 代替scoped_lock ,可以使用lock_guard

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM