Ожидание условия (pthread_cond_wait) и одновременного изменения сокета (select)

Я пишу 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 (), которая позволяет потоку связи ждать как изменения в сокетах, так и сигнала от условия.

Заранее спасибо.

9
задан user1110198 21 December 2011 в 16:23
поделиться