Асинхронное чтение из нескольких манипуляторов

Проблема

Вы хотите узнавать о наличии данных для чтения, вместо того чтобы приостанавливать процесс в ожидании ввода, как это делает <>. Такая возможность пригодится при получении данных от каналов, сокетов, устройств и других программ.

Решение

Если вас не смущают операции с битовыми векторами, представляющими наборы файловых дескрипторов, воспользуйтесь функцией select с нулевым тайм-аутом:
$rin =  '';
# Следующая строка повторяется для всех опрашиваемых манипуляторов
vec($rin, fileno(FH1), 1) = 1:
vec($rin, fileno(FH2), 1) = 1:
vec($rin, fileno(FH3), 1) = 1;
$nfound = select($rout=$rin, undef, undef, 0);
if  ($nfound)   {
  # На одном или нескольких манипуляторах имеются входные данные
  if  (vec($r,fileno(FH1),1))   {
    # Сделать что-то с FH1
  }
  if  (vec($r,fileno(FH2),1))   {
    # Сделать что-то с FH2
  }
  if (vec($r,fileno(FH3),1))  {
    # Сделать что-то с FH3
  }
}
Модуль IO::Select позволяет абстрагироваться от операций с битовыми векторами:
use IO::Select;
$select = IO::Select->new();
# Следующая строка повторяется для всех опрашиваемых манипуляторов
$select->add(*FILEHANDLE);
if (@ready = $select->can_read(0)) {
  # Имеются данные на манипуляторах из массива @ready
}

Комментарий

Функция select в действительности объединяет сразу две функции. Вызванная с одним аргументом, она изменяет текущий манипулятор вывода по умолчанию. При вызове с четырьмя аргументами она сообщает, какие файловые манипуляторы имеют входные данные или готовы получить вывод. В данном рецепте рассматривается только 4-аргументный вариант select.
Первые три аргумента select представляют собой строки, содержащие битовые векторы. Они определяют состояние файловых дескрипторов, ожидающих ввода, вывода или сообщений об ошибках (например, сведений о выходе данных за пределы диапазона для срочной передачи сокету). Четвертый аргумент определяет тайм-аут — интервал, в течение которого select ожидает изменения состояния. Нулевой тайм-аут означает немедленный опрос. Тайм-аут также равен вещественному числу секунд или undef. В последнем варианте select ждет, пока состояние изменится:
$rin = '';
vec($rin, fileno(FILEHANDLE), 1) = 1;
$nfound = select($rin, undef, undef, 0);  # Обычная проверка
if ($nfound) {
  $line = <FILEHANDLE>;
  print "I read $line";
}
Однако такое решение не идеально. Если среди передаваемых символов не встретится символ перевода строки, программа переходит в ожидание в <FILEHANDLE>.
Чтобы справиться с этой проблемой, мы последовательно читаем по одному символу и обрабатываем готовую строку при получении "\n". При этом отпадает необходимость в синхронном вызове <FILEHANDLE>.
Модуль IO::Select скрывает от вас операции с битовыми векторами. Метод IO::Select->new() возвращает новый объект, для которого можно вызвать метод add, чтобы дополнить набор новыми файловыми манипуляторами. После включениях всех интересующих вас манипуляторов вызываются функции can_read, can_write и can_exception. Функции возвращают список манипуляторов, ожидающих чтения, записи или непрочитанных срочных данных (например, информации о нарушении диапазона TCP).
Вызовы 4-аргументной версии select не должны чередоваться с вызовами каких-либо функций буферизованного вывода ( read, <>, seek, tell и т. д.). Вместо этого следует использовать sysread — вместе с sysseek, если вы хотите изменить позицию внутри файла для данного манипулятора.

См. также




2013-09-10 17:05:19

Proverte kod v komentariyah gde pro list tam oshibki detskie




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: