В многопоточных приложениях иногда возникает необходимость создать producer/consumer очередь. Наилучший вариант это использовать для этого готовую библиотеку. Если по каким-то причинам этого сделать нельзя, то такую очередь можно реализовать самому. Как правило, для этого используются механизмы синхронизации mutex и condition variables.
Интерфейс синхронизации различается от среды к среде, поэтому приходится либо писать высокоуровневую обертку, либо брать за основу интерфейс какой-то среды и реализовать его для других. К примеру, можно взять за основу интерфейс pthread
, и для Windows реализовать функции pthread_mutex_*
и pthread_cond_*
, на основе которых уже построить очередь.
Существует более легкий способ. Он заключается в том что можно создать сокет пару (socketpair) или трубу (pipe) из двух связанных дескрипторов, отдать пишущий конец трубы поставщику (producer), а читающий конец отдать всем потребителям (consumers). Если задача определена как структура фиксированной длины, то поставщик простой записью в пишущий дескриптор добавляет задачу в очередь. Потребители висят в блокирующем чтении на читающем дескрипторе. При появлении данных в трубе только один потребитель проснется и прочтет задачу.
Таким образом, используется готовый механизм синхронизации внутри ядра. Интерфейс pipe()
есть практически в любой среде. В UNIX есть также socketpair()
, легко реализуемый под WIndows.
Если очередь переполнена, то поставщик заблокируется в write()
(или send()
для сокетов). Если блокировка нежелательна, можно пишущий дескриптор перевести в неблокирующий режим.
Таким образом pipe или socketpair — это удобное кроссплатформенное решение для producer/consumer очереди в многопоточном или многопроцессном приложении, использующее готовые механизмы синхронизации в ядре.