Если я правильно понимаю описание, мне кажется, что использование круговой очереди, как вы упомянули, было бы хорошим решением IPC. Я думаю, что он может очень хорошо масштабироваться и в конечном итоге будет лучше, чем отдельные каналы или отдельная общая память для каждого клиента. Одной из нескольких проблем использования единой очереди / буфера для нескольких клиентов является синхронизация доступа к буферам. Клиент должен иметь возможность успешно читать запись в очереди без изменения ее сервером. Вот возможный механизм для реализации этого.
Для этого необходимо, чтобы сервер знал, сколько активных клиентов существует. Это, я полагаю, было бы возможно, если бы клиенты выполняли какую-либо регистрацию / вход в систему на сервере (почти наверняка это так, если они в процессе, но не обязательно так для клиентов вне процесса).
- Предположим, есть N клиентов. Для этого примера предположим 100 активных клиентов.
- Ведение двух счетных семафоров для каждой записи в циклической очереди. Если используются клиенты вне процесса, они должны быть разделены между процессами. Вызовите семафоры SemReady и SemDone.
- Используйте SemReady, чтобы указать, что буфер готов для чтения клиентами. Сервер записывает в запись буфера, а затем устанавливает значение семафора в число клиентов (в данном случае 100). Подробнее об этом чуть позже.
- Когда клиент хочет прочитать запись в очереди, он ожидает соответствующего семафора SemReady. Если начальное значение равно 100, то все 100 клиентов могут успешно получить семафор и «одновременно» прочитать данные.
- Когда клиент завершает чтение / использование записи, он увеличивает / освобождает семафор SemDone.
- Когда сервер хочет выполнить запись в буферную запись, ему необходимо убедиться в двух вещах: a) ни один из клиентов в настоящее время не читает его, и b) ни один из клиентов не начинает читать его, когда сервер пишет в него.
- Поэтому, во-первых, заблокируйте любой дальнейший доступ к буферу, ожидая семафор SemReady, пока счетчик не станет равным нулю (очевидно, используйте нулевое время ожидания). Когда он достигает нуля, сервер знает, что никакие дополнительные клиенты не начнут его читать.
- Чтобы знать, что клиенты работают с буфером, сервер использует семафор SemDone. Он проверяет SemDone и ждет, пока его значение не станет равным N минус количество ожиданий, которое он совершил в SemReady. Другими словами, если SemReady был в нуле, то это означает, что все клиенты читают запись буфера, поэтому SemDone должен быть в N (100), когда они сделаны. Если, однако, сервер ожидал 10 раз в SemReady, то SemDone должен быть в 90 (N-10), когда все клиенты сделаны.
- Вышеуказанный шаг требует некоторого времени ожидания и проверки состояния «живучести» клиента в случае сбоя / выхода из клиента после получения SemReady и до выпуска SemDone. Кроме того, необходимо учитывать возможность регистрации нового клиента на этом этапе, чтобы синхронизировать значения счетчика семафоров.
- Как только сервер обнаружил, что клиенты больше не читают буфер, он может сбросить SemDone на ноль, записать новые данные в запись и установить SemReady в N (100).
- Сполосните и повторите.
Примечание 1 Существуют и другие проблемы с синхронизацией для поддержания заголовка / хвоста кольцевой очереди, чтобы клиенты знали, где она находится.
Примечание 2 SemDone, вероятно, может быть целочисленным счетчиком, обработанным с атомными приращениями ... Я думаю, что в любом случае это возможно. Нужно немного подумать.
Примечание 3 Возможно, имеет смысл иметь несколько потоков на сервере, выполняющих запись в записи буфера. Таким образом, если серверу придется немного подождать / тайм-аут на сбойном клиенте, который начал чтение, но не завершил работу, он не заблокирует последующие записи в очереди, которые другие клиенты уже могут ожидать.