调用 fork 时缓冲 C++ 流的竞争条件——强制刷新?
Race condition of buffered C++ streams when calling fork -- force flush?
我有一个使用 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) 不适用于其他 not 可全局访问的缓冲流。此外,有时我可能会打开另一个文件,然后我可能会忘记刷新它。
这个问题有更好的解决办法吗?就像一个 刷新所有 缓冲的 C++ 流 ?
的函数
编辑
建议的解决方案是使用 C 库中的 fflush(nullptr)
来刷新所有 (C) 流。这适用于与 stdout
和 stderr
保持同步的 std::cout
和 std::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);
If the stream argument is NULL, fflush() flushes all open output streams.
我有一个使用 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) 不适用于其他 not 可全局访问的缓冲流。此外,有时我可能会打开另一个文件,然后我可能会忘记刷新它。
这个问题有更好的解决办法吗?就像一个 刷新所有 缓冲的 C++ 流 ?
的函数编辑
建议的解决方案是使用 C 库中的 fflush(nullptr)
来刷新所有 (C) 流。这适用于与 stdout
和 stderr
保持同步的 std::cout
和 std::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);
If the stream argument is NULL, fflush() flushes all open output streams.