呼叫叉时缓冲C 流的种族条件 - 力冲洗

Race condition of buffered C++ streams when calling fork -- force flush?

本文关键字:条件 缓冲 呼叫      更新时间:2023-10-16

我有一个程序,该程序用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,但这添加了不必要的flush调用。尽管这种适用于全球可访问的std::cout的作品,但我的首选解决方案(1)对其他可访问全球访问的缓冲流不起作用。此外,在某个时候,我可能会打开另一个文件,然后我可能会忘记冲洗。

有更好的解决方案解决这个问题吗?像潮红的函数一样


编辑

建议的解决方案是使用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

不必说线的数量应该相等。

有其他想法?

使用 fflush,然后传递 nullptr

来自男人:

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

如果流参数为null,则fflush()冲洗所有打开的输出流。