2015-05-14 5 views
2

У меня есть программа, которая порождает поток писателя с UNIX fork(). Это прекрасно работает, но когда буферизованный поток C++ еще не очищен, я получаю условие гонки, когда два потока выводят одни и те же данные. Следующий пример показывает, что я имею в виду:Состояние гонки буферизованных потоков C++ при вызове fork-force flush?

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) вровень std::cout вручную прямо перед вызовом fork() (предпочтительное решение), или (2) с использованием std::endl при записи потока, но это добавляет излишне много звонков flush. Хотя этот вид работ для std::cout, который доступен по всему миру, мое предпочтительное решение (1) не работает для других буферизованных потоков, которые не доступны по всему миру. Кроме того, в какой-то момент я мог бы открыть еще один файл, и я, вероятно, забыл бы его сбросить.

Есть ли лучшее решение этой проблемы? Как функция, которую сбрасывает все буферизированный C++ streams?


РЕДАКТИРОВАТЬ

Предлагаемым решения заключается в использовании fflush(nullptr) из библиотеки C, чтобы смыть все (C) потоки. Это работает для std::cout и std::cerr, которые хранятся в синхронизации с stdout и stderr, но другие буферизованные потоки С ++ будут не быть синхронизированы. Это демонстрирует проблему:

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 

Излишне говорить о том, что число строк должно быть одинаковым.

Другие идеи?

ответ

4

Используйте fflush и передайте его nullptr.

От человека:

#include <cstdio> // adapted the include for C++ 

int fflush(FILE *stream); 

Если поток аргумент NULL, то fflush() сбрасывает все открытые выходные потоки.

+0

хорошо .. не C++ буферизованные потоки и C буферизированные потоки две разные вещи? Тогда функция C, скорее всего, будет только скрывать потоки C. – user1978011

+0

На самом деле, я думаю, вы правы. Хотя это гарантированно работает для стандартных потоков вывода и ошибок (потоки C++ и C синхронизируются), я не уверен, что происходит для других файловых потоков. –

+1

Я думаю, что до тех пор, как 'sync_with_stdio (true);' для стандартных потоков, тогда вы в порядке (поскольку они будут использовать одни и те же буферы). Но я не думаю, что другие открытые потоки будут размыты. – vsoftco

Смежные вопросы