Synchronizing STD cout output multi-thread

k3oy picture k3oy · Feb 17, 2012 · Viewed 11.5k times · Source

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:

#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
my04
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
my02
my03
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?

Ps:I'm using Visual C++ 2010 Express and my cpu has 8 different cores.

Thank you for your time!

Answer

Jerry Coffin picture Jerry Coffin · Feb 17, 2012

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.

Second, instead of doing the I/O in the threads themselves, you want to create results, and do the output itself serially. 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 futures. I believe the basic idea should also work with Boost futures (upon which the standard was based) but I haven't tested that. I'd expect that some minor adjustments (e.g., to names) will be needed to work with Boost's futures.