Как реализовать независимую от платформы асинхронную запись в файл?

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

Какие варианты у меня есть?

Я думал о следующем:

  • с помощью boost:asio записать в файл, но кажется (см. этот документация), что асинхронная запись в файл находится в специфической для Windows части этой библиотеки, поэтому ее нельзя использовать.
  • С помощью boost::interprocess создать очередь сообщений, но эта документация указывает, что есть 3 метода, в которых сообщения могут быть отправлены, и все методы требуют, чтобы программа блокировала (неявно или нет), если очередь сообщений заполнена, что я не могу рисковать.
  • создавая std::deque<MESSAGES> нажать на deque из функции обратного вызова и выдать сообщения во время записи в файл (в отдельном потоке), но контейнеры STL не гарантируется потокобезопасность. Я мог бы заблокировать нажатие и удаление очереди, но мы говорим о 47 микросекундах между последовательными сообщениями, поэтому я хотел бы вообще избежать блокировок.

У кого-нибудь есть еще идеи о возможных решениях?

5

Решение

Контейнеры STL могут не быть потокобезопасными, но я никогда не сталкивался с тем, который нельзя использовать в разное время в разных потоках. Передача права собственности другому потоку кажется безопасной.

Я использовал следующее пару раз, так что я знаю, что это работает:

  • Создайте указатель на std :: vector.
  • Создайте блокировку мьютекса для защиты векторного указателя.
  • Используйте new [], чтобы создать std :: vector, а затем резервируйте () большой размер для него.

В ветке получателя:

  • Блокируйте мьютекс при добавлении элемента в очередь. Это должен быть короткий замок.
  • Добавить элемент очереди.
  • Отпустите замок.
  • Если вы чувствуете, что это сигнал переменной состояния. Я иногда нет: это зависит от дизайна. Если громкость очень высокая и на принимающей стороне нет паузы, просто пропустите условие и выполните опрос.

На потребительском потоке (пишущий диск):

  • Поищите работу, выполнив опрос или ожидая условную переменную:
  • Блокировка очереди мьютекс.
  • Посмотрите на длину очереди.
  • Если в очереди есть работа, назначьте указатель на переменную в потоке потребителя.
  • Используйте new [] и reserve (), чтобы создать новый вектор очереди и назначить его указателю очереди.
  • Разблокируйте мьютекс.
  • Уходи и пиши свои вещи на диск.
  • удалить [] использованный вектор очереди.

Теперь, в зависимости от вашей проблемы, вам может понадобиться способ блокировки. Например, в одной из моих программ, если длина очереди достигает 100 000 элементов, производящий поток просто начинает 1 секунду спать и много жаловаться. Это одна из тех вещей, которые не должны происходить, но происходят, поэтому вы должны рассмотреть это. Безо всяких ограничений он будет просто использовать всю память на машине, а затем аварийно завершать работу, быть убитым OOM или просто остановится в шторме подкачки.

2

Другие решения

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

std::deque<std::deque<MESSAGES> >

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

2