Запись одновременно в файл - PullRequest
2 голосов
/ 16 декабря 2009

У меня есть этот инструмент, в котором один лог-файл записывается несколькими процессами.

Чего я хочу добиться - это урезать файл при первом его открытии, а затем в конце все записи выполнять несколькими процессами, у которых он открыт. Все записи систематически сбрасываются и защищены от мьютекса, поэтому я не получаю беспорядочный вывод.

Сначала процесс создает файл, затем запускает последовательность других процессов, по одному, которые затем открывают файл и записывают его (мастер иногда включается с дополнительным контентом; подчиненный процесс может или не может быть открытым и писать что-то).

Я бы хотел, насколько это возможно, не использовать больше IPC, чем то, что уже существует (все, что я сейчас делаю, - это пишу в канал, созданный popen). У меня нет доступа к внешним библиотекам, кроме CRT и Win32 API, и я не хотел бы писать код сериализации.

Вот код, который показывает, куда я ушел:

// open the file. Truncate it if we're the 'master', append to it if we're a 'slave'
std::ofstream blah(filename, ios::out | (isClient ? ios:app : 0));

// do stuff...

// write stuff
myMutex.acquire();
blah << "stuff to write" << std::flush;
myMutex.release();

Ну, это не работает: хотя выходные данные подчиненного процесса упорядочены, как и ожидалось, то, что пишет мастер, либо сгруппировано, либо не в том месте, когда оно вообще существует.

У меня два вопроса: правильная ли комбинация флагов для конструктора ofstream? В любом случае, я иду по правильному пути?

Ответы [ 6 ]

1 голос
/ 23 декабря 2009

Я создал систему lil log, которая имеет свой собственный процесс и будет обрабатывать процесс записи, идея довольно проста. Процессы, использующие журналы, просто отправляют их в очередь ожидания, которую процесс журнала попытается записать в файл. Это похоже на пакетную обработку в любом приложении рендеринга в реальном времени. Таким образом, вы избавитесь от слишком большого количества операций открытия / закрытия файлов. Если я могу, я добавлю образец кода.

1 голос
/ 16 декабря 2009

Как предположил reinier, проблема была не в том, как я использую файлы, а в том, как программы ведут себя.

С ручьями все в порядке.

То, что я пропустил, - это синхронизация между ведущим и ведомым (в первом случае предполагалось, что конкретная операция была синхронной там, где ее не было).

edit: Да ладно, все еще была проблема с открытыми флагами. Процесс, открывший файл с помощью ios :: out, не перемещал указатель файла по мере необходимости (стирая текст, который записывали другие процессы), а использование seekp () полностью искажало вывод при записи в cout, поскольку другая часть кода использует cerr.

Мое окончательное решение - сохранить мьютекс и сброс, и для главного процесса открыть файл в режиме ios :: out (для создания или обрезания файла), закрыть его и снова открыть с помощью ios :: app. .

1 голос
/ 16 декабря 2009

Если вы будете записывать много данных в журнал из нескольких потоков, вам придется переосмыслить проект, поскольку все потоки будут блокироваться при попытке получить мьютекс, и, как правило, вы не хотите, чтобы ваш потоки заблокированы от выполнения работы, чтобы они могли войти. В этом случае вы захотите записать свой рабочий поток, чтобы записывать записи в очередь (для этого просто требуется перемещать содержимое в памяти), и иметь выделенный поток, чтобы извлекать записи из очереди и записывать их в вывод. Таким образом, ваши рабочие потоки блокируются на максимально короткое время.

Вы можете добиться еще больших результатов, используя асинхронный ввод-вывод, но это немного сложнее.

0 голосов
/ 29 декабря 2009

Один из вариантов - использовать ACE :: logging. Он имеет эффективную реализацию одновременной регистрации.

0 голосов
/ 16 декабря 2009

Я предлагаю блокировать так, чтобы текст полностью записывался в файл перед освобождением мьютекса. У меня были случаи, когда текст из одной задачи прерывался текстом из потока с более высоким приоритетом; выглядит не очень красиво

Кроме того, поместите формат в разделенный запятыми формат или какой-либо другой формат, который можно легко загрузить в электронную таблицу. Включите идентификатор потока и метку времени. Чередование текстовых строк показывает, как потоки взаимодействуют. Параметр ID позволяет сортировать по потоку. Метки времени могут использоваться для отображения последовательного доступа, а также продолжительности. Запись в формате, удобном для работы с электронными таблицами, позволит вам анализировать файл журнала с помощью внешнего инструмента без написания каких-либо утилит преобразования. Это очень помогло мне.

0 голосов
/ 16 декабря 2009

Как вы создаете этот мьютекс?
Чтобы это работало, это должен быть именованный мьютекс, чтобы оба процесса фактически блокировали одну и ту же вещь.
Вы можете проверить, действительно ли ваш мьютекс работает правильно с небольшим фрагментом кода, который блокирует его в одном процессе, и в другом процессе, который пытается его получить.

...