iostream thread safety, must cout and cerr be locked separately?

zaphoyd picture zaphoyd · Feb 1, 2013 · Viewed 8.5k times · Source

I understand that to avoid output intermixing access to cout and cerr by multiple threads must be synchronized. In a program that uses both cout and cerr, is it sufficient to lock them separately? or is it still unsafe to write to cout and cerr simultaneously?

Edit clarification: I understand that cout and cerr are "Thread Safe" in C++11. My question is whether or not a write to cout and a write to cerr by different threads simultaneously can interfere with each other (resulting in interleaved input and such) in the way that two writes to cout can.

Answer

Pete Becker picture Pete Becker · Feb 1, 2013

If you execute this function:

void f() {
    std::cout << "Hello, " << "world!\n";
}

from multiple threads you'll get a more-or-less random interleaving of the two strings, "Hello, " and "world\n". That's because there are two function calls, just as if you had written the code like this:

void f() {
    std::cout << "Hello, ";
    std::cout << "world!\n";
}

To prevent that interleaving, you have to add a lock:

std::mutex mtx;
void f() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Hello, " << "world!\n";
}

That is, the problem of interleaving has nothing to do with cout. It's about the code that uses it: there are two separate function calls inserting text, so unless you prevent multiple threads from executing the same code at the same time, there's a potential for a thread switch between the function calls, which is what gives you the interleaving.

Note that a mutex does not prevent thread switches. In the preceding code snippet, it prevents executing the contents of f() simultaneously from two threads; one of the threads has to wait until the other finishes.

If you're also writing to cerr, you have the same issue, and you'll get interleaved output unless you ensure that you never have two threads making these inserter function calls at the same time, and that means that both functions must use the same mutex:

std::mutex mtx;
void f() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Hello, " << "world!\n";
}

void g() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cerr << "Hello, " << "world!\n";
}