`std::osyncstream` 如何管理输出流?

How does `std::osyncstream` manage the out stream?

我想知道 std::osyncstream 对象如何防止数据竞争条件?它是否锁定了一些互斥体?

我具体说的是下面这个程序:

#include <iostream>
#include <fstream>
#include <thread>
#include <syncstream>


void worker( const std::size_t startValue, const std::size_t stopValue, std::ostream& os )
{
    for ( auto idx { startValue }; idx < stopValue; ++idx )
    {
        std::osyncstream out { os };
        out << "thread: " << std::this_thread::get_id( ) << "; work: " << idx << '\n';
    }
}

void runThreads( std::ostream& os )
{
    std::jthread t1 { worker, 10000, 20000, std::ref( os ) };
    std::jthread t2 { worker, 20000, 30000, std::ref( os ) };
}

int main( )
{
    std::ofstream file { "out.txt" };
    runThreads( file );
}

以上代码源码可查看here。虽然我做了一些细微的修改,让它变得更好更安全。

这个简单的程序将 20,000 行打印到一个文件中,而不会产生混乱的输出。

可能的输出:

thread: 2; work: 10000
thread: 3; work: 20000
thread: 2; work: 10001
thread: 2; work: 10002
thread: 2; work: 10003
thread: 2; work: 10004
thread: 2; work: 10005
thread: 2; work: 10006
.
.
.

幕后发生了什么?这两个线程如何相互通信?他们有单独的 syncstream 对象副本吗?这个对象(即out)如何管理输出流os?

Does it lock some mutex?

是的,间接的。 std::basic_osyncstream class,其中 osyncstreambasic_osyncstream<char> 形式的特例,派生自 std::basic_ostream,通常只有一个 'extra' std::basic_syncbuf class 的成员。 From cppreference:

Typical implementation of std::basic_osyncstream holds only one member: the wrapped std::basic_syncbuf.

正是basic_syncbuf对象实现了输出同步,防止了数据竞争。同样,from cppreference(加粗我的):

Typical implementation of std::basic_syncbuf holds a pointer to the wrapped std::basic_streambuf, a boolean flag indicating whether the buffer will transmit its contents to the wrapped buffer on sync (flush), a boolean flag indicating a pending flush when the policy is to not emit on sync, an internal buffer that uses Allocator (such as std::string), and a pointer to a mutex used to synchronize emit between multiple threads accessing the same wrapped stream buffer (these mutexes may be in a hash map with pointers to basic_streambuf objects used as keys).