【问题标题】:std::cout from multiple threads来自多个线程的 std::cout
【发布时间】:2020-10-02 11:20:06
【问题描述】:

我有一个应用程序,我在并行工作线程中执行昂贵的计算。为简单起见,我直接从这些线程将结果写入标准输出。

这一直很好,直到我改变了一些东西以使代码运行得更快。首先,我将 std::endl 替换为 "\n" 以防止在每一行之后刷新。我在主程序的 init 部分添加了以下几行:

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

工作线程代码的基本结构如下:

while(true) {
    // get data from job queue, protected by unique_lock on std::mutex

    // process the data

    // print results
    {
        std::lock_guard<std::mutex> lk(outputMutex_);
        std::cout << "print many results" << "\n"; // was originally std::endl
    }
}

由于这种“优化”,工人的输出偶尔会“混合”。即互斥锁没有达到预期目的。

为什么会这样?我的理解是只有一个 stdout 流缓冲区,并且数据按顺序到达相应的缓冲区,即使在释放互斥锁之前没有从该缓冲区刷新输出。不过好像不是这样的……

(我意识到在单独的线程中生成输出可能会更好,但是我需要使用另一个队列将这些结果传回,这在这里似乎没有必要)

更新:可能我的帖子不够清晰。我不关心结果的顺序。问题是(对于上面的例子)而不是这个:

print many results
print many results
print many results

我有时会:

print many print many results
results
print many results

并且 outputMutex_ 是一个静态成员,由所有工作线程共享。

【问题讨论】:

  • 您正在进行修改同一资源的未排序调用。结果是,iirc,未定义,所以它没有区别,\n 或 std::endl。
  • 这能回答你的问题吗? Synchronizing STD cout output multi-thread
  • @bipll:我不担心结果的顺序,但是尽管受到互斥锁的保护,结果并未作为实体打印
  • @underscore-d:不,不是真的。我的输出应该受到互斥锁的保护,并且我想避免将这些简单结果移动到单独的“输出”线程的开销,这也需要同步。
  • 问题中的代码sn-ps应该可以正常工作,所以问题一定出在未显示的代码中。

标签: c++ multithreading c++17 stdout mutex


【解决方案1】:

您正在通过多个线程访问 cout。对其队列的访问受到互斥锁的保护,但需要刷新。这不会自动发生(至少总是:))

std::endl 刷新 cout,'\n' 没有

或者,您可以使用std::flush 告诉 cout 刷新:

https://en.cppreference.com/w/cpp/io/manip/flush

尝试:

while(true) {
    // get data from job queue, protected by unique_lock on std::mutex

    // process the data

    // print results
    {
        std::lock_guard<std::mutex> lk(outputMutex_);
        std::cout << "print many results" << "\n"; // not flushed
        std::cout << std::flush; // flushed!
        std::cout << "print something else" << std::endl; // flushed!
    }
}

更多: https://stackoverflow.com/a/22026764/13735754

【讨论】:

  • 这确实是我的观察,但我不明白为什么冲洗很重要。这意味着有几个缓冲区(每个线程一个),而不是我假设的每个流一个。然后会出现问题,因为内容在工作迭代之间被缓冲,并在刷新时混合在输出中。如果只有一个缓冲区,那么就不会发生混合,因为内容是按行传递的,并且每一行的传递都受到互斥体的保护。
  • flushing 基本上是指 cout 缓冲区,flush 动作意味着字符串将被打印,并从内存中删除。您看到的问题是因为您正在保护对 cout 的访问,但您并没有告诉 cout 刷新。无论如何都会发生冲洗。如果您不冲洗,何时会发生不取决于您。例如。它可能在 cout 完成其内部缓冲区时发生。
  • 这可能看起来很奇怪,但原因是在某些情况下,您希望舒适地调用 std::cout,而不必为真正的编写付出高得离谱的计算代价cout.
  • 我了解刷新标准输出缓冲区的时间超出了我的控制范围。但是我不明白为什么缓冲区(只有一个)会按顺序填充...除非有多个缓冲区,每个线程一个...但我认为并非如此。
  • 我们举个例子:
猜你喜欢
  • 2013-02-08
  • 2013-08-19
  • 2012-03-09
  • 2017-04-30
  • 1970-01-01
  • 2018-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多