【问题标题】:Race condition of buffered C++ streams when calling fork -- force flush?调用 fork 时缓冲 C++ 流的竞争条件 - 强制刷新?
【发布时间】:2015-05-14 01:45:50
【问题描述】:

我有一个程序使用 UNIX fork() 生成写入器线程。这很好用,但是当缓冲的 C++ 流还没有被刷新时,我得到一个竞争条件,两个线程输出相同的数据。下面的例子说明了我的意思:

extern "C" {
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
}

#define W 10

#include <iostream>

int main(void)
{
   pid_t pid;
   int status;
   for (int i = 0; i < (1 << W); i++) {

      // spawn a child after adding to the std::cout buffer
      if (i == (1 << (W - 1))) {
         // std::cout.flush(); // (1)
         pid = fork();
         if (!pid)
            break;
      }

      // join the child thread after a while
      if (i == 3 * (1 << (W - 2)))
         waitpid(pid, &status, 0);

      // print stuff to the stream
      std::cout << i << '\n';
      // std::cout << i << std::endl; // (2)
   }
   return EXIT_SUCCESS;
}

所以我尝试的解决方法是(1)在调用fork()(首选解决方案)之前手动刷新std::cout,或者(2)在写入流时使用std::endl,但这会增加不必要的@ 987654326@ 电话。尽管这种方法适用于全局可访问的std::cout,但我的首选解决方案 (1) 不适用于 不可 全局可访问的其他缓冲流。此外,在某些时候我可能会打开另一个文件,然后我可能会忘记刷新它。

这个问题有更好的解决方案吗?就像一个刷新所有缓冲C++流的函数?


编辑

建议的解决方案是使用 C 库中的 fflush(nullptr) 来刷新所有 (C) 流。这适用于与stdoutstderr 保持同步的std::coutstd::cerr,但其他C++ 缓冲流将同步。这说明了问题:

extern "C" {
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
}

#include <iostream>
#include <fstream>

#define W 10

int main(void)
{
   pid_t pid;
   int status;
   std::ofstream fout("foo");

   for (int i = 0; i < (1 << W); i++) {

      if (i == (1 << (W - 1))) {
         fflush(nullptr);  // this works for std::{cout,cerr} but not files
         pid = fork();
         if (!pid)
            return EXIT_SUCCESS;
      }

      if (i == 3 * (1 << (W - 2)))
         waitpid(pid, &status, 0);

      fout << i << '\n';
      std::cout << i << '\n';
   }
   fout.close();
   return EXIT_SUCCESS;
}

在我的系统上我得到

$ ./a.out 1>bar; wc -l foo bar
1536 foo
1024 bar

行数不用说应该相等。

还有什么想法吗?

【问题讨论】:

    标签: c++ multithreading file-io fork flush


    【解决方案1】:

    使用fflush,并通过nullptr

    来自男人:

    #include <cstdio> // adapted the include for C++
    
    int fflush(FILE *stream);
    

    如果流参数为 NULL,则 fflush() 刷新所有打开的输出流。

    【讨论】:

    • 好吧.. c++ 缓冲流和 C 缓冲流不是两个不同的东西吗?那么 C 函数很可能只会刷新 C 流。
    • 我认为只要sync_with_stdio(true); 用于标准流,就可以了(因为它们将使用相同的缓冲区)。但我认为其他打开的流不会被刷新。
    猜你喜欢
    • 1970-01-01
    • 2010-09-25
    • 2019-09-28
    • 2012-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-25
    • 2013-02-27
    相关资源
    最近更新 更多