【问题标题】:Corrupted output with C++, cin, cout, threads and sync_with_stdio使用 C++、cin、cout、线程和 sync_with_stdio 损坏的输出
【发布时间】:2012-12-10 05:09:18
【问题描述】:

我正在尝试用 C++ 编写一个程序,以尽可能快的方式处理大量数据包。所有来自标准的数据包都应该被尽可能快地读取,从池中发送到一个线程进行处理,然后处理到将数据包写入标准输出的输出线程。

当您在 C++ 中使用标准输入和输出时,建议在任何输入或输出之前调用std::ios_base::sync_with_stdio(false) 函数。在某些环境中,这实现了很大的加速,尽管您应该避免在调用后使用标准 C 函数进行输入/输出。

嗯,这似乎在单个线程中完美运行。但正如我所说,我的意图是使用一个线程进行输入,一个线程用于输出,多个线程进行并行处理。我观察到输出存在一些问题。这是输出线程(非常简化):

void PacketDispatcher::thread_process_output(OutputQueue& output_queue) {
    std::vector<Packet> packet_list;
    while(output_queue.get(packet_list)) {
        for (const auto& packet: packet_list) {
            std::cout << "Packet id = " << packet.id << "\n";
        }
    }
    std::cout.flush();
}

如果我使用 std::endl 而不是 "\n" 损坏较少,但 std::endl 会强制刷新流,从而影响性能这种情况(问题没有解决,只是最小化了)。

这是程序中使用 std::cout 的唯一点,但如果我在程序开始时调用 std::ios_base::sync_with_stdio(false),我会得到明显的加速,但我的输出总是以某种方式损坏:

Packet id = Packet id = 4
Packet id = 5
Packet id = 6
Packet id = 7
Packet id = 8
Packet id = 9
Packet id = 10

那么,问题出在哪里? C++ 不能使用快速标准输入/输出进行多线程吗?

【问题讨论】:

    标签: c++ multithreading input corruption output


    【解决方案1】:

    我终于找到了罪魁祸首。如果您在 Internet 上搜索,很多网站都建议使用 sync_with_stdio 调用,但他们不谈论线程。

    其他网站谈论 iostream 和线程,例如 this one,但这并不能解释为什么我在仅一个线程中使用 std::cin 和 std ::cout 也在它自己的线程中

    问题在于,在内部,std::cin 输入线程正在调用 std::cout 以刷新其缓冲区,但由于未与 mutex 或类似内容同步的流,输出已损坏。如果缓冲区在做不同的事情,我为什么要同步它们?为什么 std::cin 搞乱了 std::cout?

    在 C++ 中,默认情况下,标准流 cin、cerr 和 clog 绑定到 cout。这是什么意思?这意味着当您尝试从 cin 读取时,首先它会强制刷新到 cout。有时这很有用,您可以阅读here

    但就我而言,这导致了一些严重的问题,那么,如何解开流?使用tie method 非常简单:

    std::ios_base::sync_with_stdio(false);
    
    std::cin.tie(nullptr);
    std::cerr.tie(nullptr);
    

    或者如果你的编译器不支持 C++11:

    std::ios_base::sync_with_stdio(false);
    
    std::cin.tie(static_cast<ostream*>(0));
    std::cerr.tie(static_cast<ostream*>(0));
    

    有了这个改变,我的输出现在是正确的:

    Packet id = 1
    Packet id = 2
    Packet id = 3
    Packet id = 4
    Packet id = 5
    Packet id = 6
    Packet id = 7
    Packet id = 8
    Packet id = 9
    Packet id = 10
    

    因为它避免了每次使用 std::cin 时都进行刷新,所以它也更快:-)

    【讨论】:

    • 好收获。所以stdout的fflush是有的,因为很多人都是这样做的:cout &lt;&lt; "What's your name?"; cin &gt;&gt; name;,没有输出的时候会糊涂。一些 C 库没有“读前刷新”,这至少会导致初学者感到困惑。
    • 顺便说一句,您不需要显式转换 static_cast&lt;ostream*&gt;(0);。您只需将0 传递为std::cin.tie(0);,您还应该保存返回的流,因此当您完成后,您可以将tie 传递到流中。
    • 纳瓦兹,你是对的。我试图使所有类型都安全,但在这种情况下,演员并不是真的必要。虽然我的程序中不需要它,但保存返回的指针是一个很好的建议。
    猜你喜欢
    • 2014-07-20
    • 2015-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-30
    • 2021-11-06
    • 1970-01-01
    • 2012-08-02
    相关资源
    最近更新 更多