[英]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
my04my04
my03my03
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
my02my02
my03my03
my04my04
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.