2009-07-02 2 views
5

Я пытаюсь создать службу, которая содержит множество клиентских и серверных сокетов (серверная служба, а также клиенты, которые подключаются к управляемым компонентам и сохраняются), которые синхронно опрошены через IO::Select. Идея заключалась в том, чтобы обрабатывать потребности обработки ввода-вывода и/или запроса, которые возникают через пулы рабочих потоков.Неблокирующие операции ввода/вывода в Perl ограничены одним потоком? Хороший дизайн?

Ключевое слово shared, которое обеспечивает совместное использование данных по потокам в Perl (threads::shared), имеет свои пределы - ссылки на дескрипторы не входят в число примитивов, которые могут использоваться совместно.

Прежде чем я понял, что ручки и/или обрабатывать ссылки не могут быть разделены, план должен был иметь select() нить, которая ухаживает опроса, а затем помещает соответствующие ручки в определенном ThreadQueue ы распространяться через пул потоков для на самом деле делать чтение и письмо. (Я, конечно же, проектировал это так, чтобы модификация фактических наборов дескрипторов, используемых select, была бы потокобезопасной и выполнялась только в одном потоке - то же самое, что работает select(), и поэтому никогда, пока она работает, очевидно.)

Это не похоже, что это произойдет сейчас, потому что сами ручки не могут быть разделены, поэтому опрос, а также чтение и письмо должны произойти из одного потока. Есть ли обходной путь для этого? Я имею в виду разложение фактических системных вызовов по потокам; Очевидно, существуют способы использования очередей и буферов для получения данных, созданных в других потоках и фактически отправленных в других.

Одна из проблем, возникающих из-за этой ситуации, заключается в том, что я должен дать тайм-ауту select() и ожидать, что он будет достаточно высоким, чтобы не вызывать каких-либо проблем с опросом довольно большого набора дескрипторов, хотя достаточно низко, чтобы не вводить слишком много задержек в моем цикле событий синхронизации - хотя, я понимаю, что если в процессе опроса обнаружено фактическое членство в модуле ввода-вывода, select() вернется рано, что частично смягчит проблему. Я бы предпочел иметь какой-то способ пробуждения select() от другого потока, но поскольку ручки не могут быть разделены, я не могу легко думать о способе этого делать и не видеть значения при этом; что другой поток будет знать о том, когда уместно просыпаться select() в любом случае?

Если нет обходного пути, что такое хороший шаблон проектирования для этого типа услуг в Perl? У меня есть требование для довольно высокого уровня масштабируемости и параллельного ввода-вывода, и по этой причине вышли неблокирующий маршрут, а не просто порождали потоки для каждого прослушивающего сокета и/или клиентского и/или серверного процессов, так как многие люди, которые в наши дни не имеют отношения к сокетам - это, похоже, стандартная практика на Java-землях, и никто, кажется, не заботится о java.nio.* за пределами узкой сферы системно-ориентированного программирования. Возможно, это просто мое впечатление. Во всяком случае, я не хочу так поступать.

Итак, с точки зрения опытного программиста систем Perl, как организовать этот материал? Монолитные потоки ввода-вывода + потоки чистого рабочего (не-ввода-вывода) + множество очередей? Какой-то умный хак? Любая безопасность нитей требует, чтобы смотреть за пределы того, что я уже перечислил? Есть ли способ лучше? У меня есть большой опыт архивирования такого рода программ на C, но не с идиомами Perl или характеристиками времени исполнения.

EDIT: P.S. Мне определенно пришло в голову, что, возможно, программа с этими требованиями к производительности, и этот дизайн просто не должен быть написан на Perl. Но я вижу в Perl очень много очень сложных сервисов, поэтому я не уверен в этом.

ответ

5

Брекетинг ваших нескольких более крупных вопросов дизайна, я могу предложить несколько подходов к совместному использованию дескрипторов файлов через потоки perl.

Можно передать $client в тему запуск подпрограммы или просто ссылаться на него в новом потоке:

$client = $server_socket->accept(); 

threads->new(\&handle_client, $client); 
async { handle_client($client) }; 
# $client will be closed only when all threads' references 
# to it pass out of scope. 

Для Thread::Queue дизайна, один может enqueue() лежащих в основе ФД:

$q->enqueue(POSIX::dup(fileno $client)); 
# we dup(2) so that $client may safely go out of scope, 
# closing its underlying fd but not the duplicate thereof 

async { 
    my $client = IO::Handle->new_from_fd($q->dequeue, "r+"); 
    handle_client($client); 
}; 

Или один могут использовать только fds и битовую векторную форму Perl's select.

+0

Интересные предложения. Мне особенно нравится подход к отправке базового FD и последующее построение дескриптора. Можете ли вы предложить способ инициализации дескриптора файла, а затем назначить его IO :: Socket позже, внутри потока? Я бы предпочел не создавать сокеты в одном потоке, а затем манипулировать ими в другом; возможно ли сделать что-то общее, например IO :: Handle-> new? –

+0

Alex, без псевдокода, я не уверен, что понимаю подробности вашего вопроса здесь. Однако «использовать IO :: Socket :: INET» и «IO :: Socket :: INET-> new_from_fd ($ my_duplicated_fd,« r + »)» вы получите объект IO :: Socket. – pilcrow

+0

О, я не знал, что IO :: Socket :: INET является подклассом IO :: Handle. Имеет смысл. –

 Смежные вопросы

  • Нет связанных вопросов^_^