С ++ несколько процессов, записывающих в один и тот же файл - межпроцессный мьютекс?

У меня такой вопрос: как лучше всего (или, по крайней мере, эффективно) писать в файл из нескольких процессов?

Примечание. Я использую C ++ 11 и хочу, чтобы он работал на любой платформе (т. е. только на чистом коде C ++).

Я провел небольшое исследование и пришел к выводу:

  1. В моих процессах у меня несколько потоков. Это легко выполняется в каждом процессе с помощью мьютекса для сериализации доступа к файлу.
  2. Мьютекс или условную переменную c ++ / c ++ 11 нельзя использовать для сериализации между процессами.
  3. Мне нужен какой-то внешний файл семафора / блокировки, чтобы действовать как «мьютекс» ... но я не уверен, как это сделать.

Я видел, как приложения при использовании используют такие вещи, как создание файла ".lock". Но для множественного быстрого доступа кажется, что это может не сработать (т.е. после того, как один процесс решил, что файл не существует, другой может его создать, а затем первый процесс также попытается создать его), потому что операция по тестированию и созданию файла не атомный.

Примечание. Каждый процесс всегда записывает одну целую строку за раз. Я думал, что этого может быть достаточно, чтобы сделать операцию «атомарной» (в том смысле, что вся строка будет буферизована перед следующей), но, похоже, это не так (если у меня нет неправильного кода), поскольку я ( редко) получается покоробленная строчка. Вот фрагмент кода, как я пишу (если это актуально):

// in c'tor
m_osFile.open("test.txt", std::fstream::out | std::fstream::app)

// in write func (std::string data)
osFile << data<< std::endl;

Это, должно быть, обычная проблема, но я еще не нашел рабочего решения для нее. Приветствуются любые фрагменты кода.


person code_fodder    schedule 20.03.2018    source источник
comment
Похоже на вариацию на тему читатель-писатель. - en.wikipedia.org/wiki/Readers%E2%80%93writers_problem < / а>   -  person SenselessCoder    schedule 20.03.2018
comment
Google c ++ назвал мьютекс для основных обращений. 11000 посещений только этого веб-сайта, он хорошо освещен.   -  person Hans Passant    schedule 20.03.2018
comment
@HansPassant, извините, я также хотел сказать, что не хочу использовать ускорение, если я могу этого избежать   -  person code_fodder    schedule 20.03.2018
comment
Ваша ОС поддерживает именованные мьютексы. Возможно, вы также забыли упомянуть, что не хотите его использовать, если можете этого избежать?   -  person Hans Passant    schedule 20.03.2018
comment
Один из вариантов может заключаться в том, чтобы родительский процесс действовал в качестве диспетчера, создавая канал для каждого процесса для записи и сопоставляя выходные данные потокобезопасным способом.   -  person Gem Taylor    schedule 20.03.2018
comment
@HansPassant - Я не уверен, что понимаю вашу точку зрения?   -  person code_fodder    schedule 20.03.2018
comment
@GemTaylor хм ... звучит интересно - я думаю, это как отдельный процесс, в который я отправляю весь вывод, и он занимается сериализацией?   -  person code_fodder    schedule 20.03.2018
comment
@code_fodder - точно - у вас все еще есть проблема с получением этого дескриптора файла для вашего целевого процесса, что легко для реальных дочерних процессов и немного сложно для дочерних исполняемых файлов.   -  person Gem Taylor    schedule 20.03.2018
comment
Как насчет того, чтобы открыть файл с флагом блокировки, закрыть после завершения записи и сделать то же самое для всего процесса?   -  person Griffin    schedule 20.03.2018


Ответы (4)


У меня такой вопрос: как лучше всего (или, по крайней мере, эффективно) писать в файл из нескольких процессов?

Лучший способ ... не делай этого!

Это действительно похоже на журнал (добавление). Я бы просто позволил каждому процессу записывать свой собственный файл, а затем объединять их, когда это необходимо. По крайней мере, это распространенный подход, и в этом его обоснование.

Никакая внутрипроцессная блокировка работать не будет. Открытые файлы имеют буферизацию на уровне ОС даже после закрытия в некоторых ОС (Windows).

Вы не можете выполнить блокировку файлов, если хотите переносимое решение («Я хочу, чтобы это работало на любой платформе»): вы столкнетесь даже с возможными потерями производительности / неопределенным поведением в зависимости от используемой файловой системы (например: samba, NFS) .

Одновременная и надежная запись в один файл на самом деле сегодня является системно-зависимой деятельностью.

Я не имею в виду, что это невозможно - движки БД и другие приложения делают это надежно, но это настраиваемая операция.

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

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

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

person Sigi    schedule 20.03.2018
comment
Спасибо, +1 за объяснение и альтернативные варианты. На самом деле идея создания нескольких файлов не так уж плоха, но мне нужно поставить отметку времени, объединить, упорядочить и переформатировать файлы (удалить отметки времени) ... но все это довольно простые действия по постобработке (или по крайней мере, я вернулся в то место, где я знаю, что, черт возьми, делаю!) - person code_fodder; 20.03.2018
comment
еще один совет: google для слияния журналов или сбора журналов, поскольку эта операция действительно частая, уже доступно множество инструментов. - person Sigi; 20.03.2018

Обычно операционная система предоставляет специальные функции для блокировки файлов, которые гарантированно являются атомарными (например, lockf в Linux или LockFile (Ex) в Windows). На данный момент стандартная библиотека C ++ не предоставляет такой функциональности, поэтому независимый от платформы подход к таким возможностям обеспечивается, например, Boost.Interprocess.

person Jodocus    schedule 20.03.2018
comment
Спасибо за это. Звучит так, будто мне может понадобиться повышение ... но я подумал, может, я смогу реализовать простой класс, чтобы сделать это, вместо того, чтобы тянуть библиотеки повышения ... хотя, может быть, проще просто получить ускорение, я думаю? - person code_fodder; 20.03.2018

Я могу представить себе два сценария. Поскольку вы не указали в своих вопросах, как порождаются процессы, я представляю две ситуации:

  1. Ваш первый процесс порождает второй (например, с использованием fork()).
  2. Эти два процесса создаются отдельно в вашей среде.

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

Второй сценарий немного сложнее, он требует, чтобы каждый процесс признавал существование другого. Подобный вопрос уже обсуждался здесь, где есть интересная ссылка на как избежать состояния гонки. Я бы также подумал о проверке флагов O_EXCL и O_CREAT для этой цели.

person Alexander Pane    schedule 20.03.2018
comment
В любом случае я согласен с тем, что лучший ответ - это тот, который дал @Sigismondo. Обычно не рекомендуется иметь разные процессы, записывающие в один и тот же файл, поскольку процессы не имеют общих файловых дескрипторов; однако потоки делают это, поскольку единственный ресурс, который потоки одного процесса не используют совместно, - это стек. - person Alexander Pane; 20.03.2018

Вы можете объявить свой файловый дескриптор и связанный с ним мьютекс (условие?) В общей памяти между всеми процессами.

person Churam    schedule 20.03.2018