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

3

我想知道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 );
}

上述代码的源代码可以在此处查看。尽管我进行了轻微修改以使其更好、更安全。

这个简单的程序将20000行内容输出到文件中,而不会产生混乱的输出。

可能的输出结果:

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


2
这似乎是一篇关于Rainer Grimm的同步输出流的好文章。 - rturrado
1个回答

2

它会锁定一些互斥量吗?

是的,间接地。 std::basic_osyncstream 类是 osyncstream 的一个特化形式,即 basic_osyncstream<char>,它派生自 std::basic_ostream 并通常只有一个“额外”的成员,即 std::basic_syncbuf 类。根据cppreference

std::basic_osyncstream 的典型实现仅持有一个成员:被包装的 std::basic_syncbuf。

正是该 basic_syncbuf 对象实现了输出同步,防止数据竞争。再次引用cppreference(我加粗):

std::basic_syncbuf 的典型实现持有指向被包装的 std::basic_streambuf 的指针,一个布尔标志,指示缓冲区是否在 sync(刷新)时将其内容传输到被包装的缓冲区,一个布尔标志,指示当策略为不在 sync 上发出信号时是否存在挂起的刷新,使用 Allocator(例如 std::string)的内部缓冲区,以及指向用于在多个访问同一包装流缓冲区的线程之间同步发出信号的互斥量的指针(这些互斥量可能在哈希映射中,其中指向用作键的 basic_streambuf 对象的指针)。


“wrapped stream buffer”,这是所有osyncstream对象都可以访问并尝试修改的缓冲区吗? - digito_evo
1
@digito_evo 嗯,我既不是标准委员会的一部分,也不是实现者;然而,我会假设每个不同流的实例都可以访问不同的互斥锁/缓冲区。因此,使用std::cout创建的每个实例都将共享相同的sync_buffer。 - Adrian Mole

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接