Я пишу POSIX-совместимый многопоточный сервер на c / c ++, который должен иметь возможность асинхронно принимать, читать и записывать большое количество подключений. На сервере есть несколько рабочих потоков, которые выполняют задачи и иногда (и непредсказуемо) помещают данные в очередь для записи в сокеты. Данные также иногда (и непредсказуемо) записываются в сокеты клиентами, поэтому сервер также должен читать асинхронно. Один из очевидных способов сделать это - дать каждому соединению поток, который читает и записывает из / в свой сокет; однако это уродливо, поскольку каждое соединение может сохраняться в течение долгого времени, и серверу, таким образом, может потребоваться удерживать сотни или тысячи потоков только для того, чтобы отслеживать соединения.
Лучшим подходом было бы иметь один поток, который обрабатывал бы все коммуникации с помощью функций select () / pselect (). То есть один поток ожидает, что любой сокет станет доступным для чтения, а затем порождает задание для обработки ввода, которое будет обрабатываться пулом других потоков всякий раз, когда ввод доступен. Всякий раз, когда другие рабочие потоки производят вывод для соединения, он ставится в очередь, а коммуникационный поток ожидает, пока этот сокет станет доступным для записи, прежде чем записывать его.
Проблема заключается в том, что поток связи может ожидать в функции select () или pselect (), когда выходные данные помещаются в очередь рабочими потоками сервера. Возможно, что, если в течение нескольких секунд или минут вход не поступает, блок вывода в очереди будет просто ждать завершения коммуникационного потока select () ing. Однако этого не должно происходить - данные должны быть записаны как можно скорее.
Прямо сейчас я вижу пару решений, ориентированных на многопоточность. Один из них состоит в том, чтобы поток связи был занят ожиданием ввода и обновлял список сокетов, которые он ожидает для записи, каждые десятые доли секунды или около того. Это не оптимально, так как предполагает ожидание при занятости, но это сработает. Другой вариант - использовать pselect () и отправлять сигнал USR1 (или что-то подобное) всякий раз, когда новый вывод помещен в очередь, позволяя потоку связи немедленно обновлять список сокетов, которые он ожидает для статуса записи. Я предпочитаю последнее здесь, но все же не люблю использовать сигнал для чего-то, что должно быть условием (pthread_cond_t). Еще один вариант - включить в список файловых дескрипторов, которых ожидает select (), фиктивный файл, в который мы записываем один байт всякий раз, когда нужно добавить сокет в доступный для записи fd_set для select (); это разбудит коммуникационный сервер, потому что этот конкретный фиктивный файл будет доступен для чтения, что позволит коммуникационному потоку немедленно обновить его доступный для записи fd_set.
Я интуитивно чувствую, что второй подход (с сигналом) - это «самый правильный» способ программирования сервера, но мне любопытно, знает ли кто-нибудь, какой из вышеперечисленных является наиболее эффективным, вообще говоря, будь то один из выше вызовет состояния гонки, о которых я не знаю, или если кто-то знает более общее решение этой проблемы. Что мне действительно нужно, так это функция pthread_cond_wait_and_select (), которая позволяет потоку связи ждать как изменения в сокетах, так и сигнала от условия.
Заранее спасибо.