Ограничены ли неблокирующие операции ввода-вывода в Perl одним потоком?Хороший дизайн?

StackOverflow https://stackoverflow.com/questions/1074713

Вопрос

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

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

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

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

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

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

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

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

Это было полезно?

Решение

Заключая в квадратные скобки несколько ваших более масштабных вопросов по дизайну, я могу предложить несколько подходов к совместному использованию дескрипторов файлов в потоках 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() лежащий в основе fd:

$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 select.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top