Чтение онлайн

ЖАНРЫ

Программирование на языке Ruby
Шрифт:

IO.popen("-")

do |mypipe|

 if mypipe

puts "Я родитель: pid = #{Process.pid}"

listen = mypipe.gets

puts listen

 else

puts "Я потомок: pid = #{Process.pid}"

 end

end

# Печатается:

# Я родитель: pid = 10580

#
Я потомок: pid = 10582

Метод

pipe
возвращает также два конца канала, связанных между собой. В следующем примере мы создаем два потока, один из которых передает сообщение другому (то самое сообщение, которое Сэмюэль Морзе впервые послал по телеграфу). Если вы не знаете, что такое потоки, обратитесь к главе 3.

pipe = IO.pipe

reader = pipe[0]

writer = pipe[1]

str = nil

thread1 = Thread.new(reader,writer) do |reader,writer|

 # writer.close_write

 str = reader.gets

 reader.close

end

thread2 = Thread.new(reader,writer) do |reader,writer|

 # reader.close_read

 writer.puts("What hath God wrought?")

 writer.close

end

thread1.join

thread2.join

puts str # What hath God wrought?

10.1.14. Специальные операции ввода/вывода

В Ruby можно выполнять низкоуровневые операции ввода/вывода. Мы только упомянем о существовании таких методов; если вы собираетесь ими пользоваться, имейте в виду, что некоторые машиннозависимы (различаются даже в разных версиях UNIX).

Метод

ioctl
принимает два аргумента: целое число, определяющее операцию, и целое число либо строку, представляющую параметр этой операции.

Метод

fcntl
также предназначен для низкоуровневого управления файловыми потоками системно зависимым образом. Он принимает такие же параметры, как
ioctl
.

Метод

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

Метод

syscall
из модуля
Kernel
принимает по меньшей мере один целочисленный параметр (а всего до девяти целочисленных или строковых параметров). Первый параметр определяет выполняемую операцию ввода/вывода.

Метод

fileno
возвращает обычный файловый дескриптор, ассоциированный с потоком ввода/вывода. Это наименее системно зависимый из всех перечислениях выше методов.

desc = $stderr.fileno # 2

10.1.15.

Неблокирующий ввод/вывод

«За кулисами» Ruby предпринимает согласованные меры, чтобы операции ввода/вывода не блокировали выполнение программы. В большинстве случаев для управления вводом/выводом можно пользоваться потоками — один поток может выполнить блокирующую операцию, а второй будет продолжать работу.

Это немного противоречит интуиции. Потоки Ruby работают в том же процессе, они не являются платформенными потоками. Быть может, вам кажется, что блокирующая операция ввода/вывода должна приостанавливать весь процесс, а значит, и все его потоки. Это не так — Ruby аккуратно управляет вводом/выводом прозрачно для программиста.

Но если вы все же хотите включить неблокирующий режим ввода/вывода, такая возможность есть. Небольшая библиотека

io/nonblock
предоставляет методы чтения и установки для объекта
IO
, представляющего блочное устройство:

require 'io/nonblock'

# ...

test = mysock.nonblock? # false

mysock.nonblock = true # Отключить блокирующий режим.

# ...

mysock.nonblock = false # Снова включить его.

mysock.nonblock { some_operation(mysock) }

# Выполнить some_operation в неблокирующем режиме.

mysock.nonblock(false) { other_operation(mysock) }

# Выполнить other_operation в блокирующем режиме.

10.1.16. Применение метода readpartial

Метод

readpartial
появился сравнительно недавно с целью упростить ввод/вывод при определенных условиях. Он может использоваться с любыми потоками, например с сокетами.

Параметр «максимальная длина» (max length) обязателен. Если задан параметр buffer, то он должен ссылаться на строку, в которой будут храниться данные.

data = sock.readpartial(128) # Читать не более 128 байтов.

Метод

readpartial
игнорирует установленный режим блокировки ввода/вывода. Он может блокировать программу, но лишь при выполнении следующих условий: буфер объекта IO пуст, в потоке ничего нет и поток еще не достиг конца файла.

Таким образом, если в потоке есть данные, то

readpartial
не будет блокировать программу. Он читает не более указанного числа байтов, а если байтов оказалось меньше, то прочитает их и продолжит выполнение.

Если в потоке нет данных, но при этом достигнут конец файла, то

readpartial
немедленно возбуждает исключение
EOFError
.

Если вызов блокирующий, то он ожидает, пока не произойдет одно из двух событий: придут новые данные или обнаружится конец файла. Если поступают данные, метод возвращает их вызывающей программе, а в случае обнаружения конца файла возбуждает исключение

EOFError
.

При вызове метода

sysread
в блокирующем режиме он ведет себя похоже на
readpartial
. Если буфер пуст, их поведение вообще идентично.

Поделиться с друзьями: