Если вы хотите максимальной гибкости в модели синхронизации условных переменных POSIX, вы должны избегать написания модулей, которые передают события своим пользователям только посредством предоставления условной переменной. (Затем вы, по сути, заново изобрели семафор.)
Активные модули должны быть спроектированы таким образом, чтобы их интерфейсы обеспечивали уведомления о событиях обратного вызова через зарегистрированные функции: и, при необходимости, чтобы можно было зарегистрировать несколько обратных вызовов.
Клиент нескольких модулей регистрирует обратный вызов с каждым из них. Все они могут быть направлены в обычное место, где они блокируют один и тот же мьютекс, изменяют некоторое состояние, разблокируют и нажимают на одну и ту же переменную условия.
Этот дизайн также дает возможность того, что, если объем работы, выполненной в ответ на событие, достаточно мал, возможно, это можно сделать просто в контексте обратного вызова.
Обратные вызовы также имеют некоторые преимущества при отладке. Вы можете поставить точку останова на событие, которое приходит в форме обратного вызова, и посмотреть, как оно было сгенерировано. Если вы ставите точку останова на событие, которое приходит как пробуждение семафора, или с помощью какого-либо механизма передачи сообщений, трассировка вызова не раскрывает источник события.
При этом вы можете создавать свои собственные примитивы синхронизации с мьютексами и условными переменными, которые поддерживают ожидание нескольких объектов. Эти примитивы синхронизации могут быть внутренне основаны на обратных вызовах способом, который невидим для остальной части приложения.
Суть в том, что для каждого объекта, которого поток хочет ждать, операция ожидания ставит в очередь интерфейс обратного вызова с этим объектом. Когда объект получает сигнал, он вызывает все свои зарегистрированные обратные вызовы. Пробужденные потоки исключают из очереди все интерфейсы обратного вызова и просматривают некоторые флаги состояния в каждом из них, чтобы увидеть, какие объекты сигнализируют.