У меня есть рабочий поток, который слушает сокет TCP для входящего трафика и буферизует полученные данные для основного потока для доступа (позвольте этому сокету A). Тем не менее, рабочий поток также должен выполнять некоторые регулярные операции (скажем, один раз в секунду), даже если нет данных. Поэтому я использую select()
с таймаутом, так что мне не нужно проводить опрос, (Обратите внимание, что вызов receive()
в неблокирующем сокете, а затем сон в течение секунды не подходит: входящие данные должны быть немедленно доступны для основного потока, хотя основной поток не всегда сможет обрабатывать его сразу, следовательно, необходимость буферизации.)
Теперь я также должен иметь возможность сигнализировать рабочий поток, чтобы сделать что-то другое сразу; из основного потока мне нужно немедленно вернуть рабочий поток select()
. На данный момент я решил это следующим образом (подход в основном принят из здесь и здесь):
При запуске программы рабочий поток создает для этой цели дополнительный сокет типа дейтаграммы (UDP) и привязывает его к некоторому случайному порту (позвольте этому сокету B). Аналогично, основной поток создает сокет для датаграммы для отправки. В своем вызове select()
рабочий поток теперь перечисляет как A, так и B в fd_set
. Когда основной поток должен сигнализировать, он sendto()
пару байтов в соответствующий порт на localhost
. Вернитесь в рабочий поток, если B останется в fd_set
после возврата select()
, тогда вызывается recvfrom()
, и полученные байты просто игнорируются.
Это, кажется, работает очень хорошо, но я не могу сказать, что мне нравится решение, в основном потому, что для этого требуется привязать дополнительный порт для B, а также потому, что он добавляет несколько дополнительных вызовов API сокетов, которые могут не срабатывать, я думаю - и я на самом деле не чувствую, как выдумывать соответствующие действия для каждого из случаев.
Я думаю, что в идеале я хотел бы назвать некоторую функцию, которая принимает A как входную, и ничего не делает, за исключением того, что возвращает select()
сразу. Однако я не знаю такой функции. (Я думаю, я мог бы, например, shutdown()
сокет, но побочные эффекты не очень приемлемы:)
Если это невозможно, вторым лучшим вариантом будет создание B, который намного dummier, чем настоящий UDP-сокет, и на самом деле не требует выделения каких-либо ограниченных ресурсов (за пределами разумного объема памяти). Я полагаю, что сокеты домена Unix будут делать именно это, но: решение не должно быть гораздо менее кросс-платформенным, чем то, что у меня есть сейчас, хотя некоторые умеренные количество вещей #ifdef
в порядке. (Я ориентируюсь главным образом на Windows и Linux - и, кстати, написание С++.)
Пожалуйста, не предлагайте рефакторинг, чтобы избавиться от двух отдельных потоков. Эта конструкция необходима, потому что основной поток может быть заблокирован в течение длительного времени (например, делать некоторые интенсивные вычисления), и я не могу начать периодически вызывать receive()
из самого внутреннего цикла вычисления), и в то же время кому-то нужно буферизировать входящие данные (и по причинам, не зависящим от того, что я могу контролировать, он не может быть отправителем).
Теперь, когда я писал это, я понял, что кто-то определенно будет отвечать просто "Boost.Asio ", поэтому я просто получил свой сначала посмотрим на это... Не удалось найти очевидное решение. Заметьте, что я также не могу (легко) повлиять на то, как создается сокет A, но я должен позволить другим объектам обернуть его, если это необходимо.