Асинхронное чтение из нескольких манипуляторов
Проблема
Вы хотите узнавать о наличии данных для чтения, вместо того чтобы приостанавливать
процесс в ожидании ввода, как это делает <>. Такая возможность пригодится при получении
данных от каналов, сокетов, устройств и других программ.
Решение
Если вас не смущают операции с битовыми векторами, представляющими наборы файловых
дескрипторов, воспользуйтесь функцией 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, если вы хотите изменить позицию внутри файла для
данного манипулятора.
См. также
Proverte kod v komentariyah gde pro list tam oshibki detskie
Оставить комментарий:
|
|