当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Ruby Kernel.select用法及代码示例


本文简要介绍ruby语言中 Kernel.select 的用法。

用法

select(read_array [, write_array [, error_array [, timeout]]]) → array or nil

调用 select(2) 系统调用。它监视给定的 IO 对象数组,等待一个或多个 IO 对象准备好读取、准备好写入并分别有未决异常,并返回一个包含这些 IO 对象数组的数组。如果给出可选的timeout 值并且在timeout 秒内没有 IO 对象准备好,它将返回nil

IO.select 查看 IO 对象的缓冲区以测试可读性。如果 IO 缓冲区不为空, IO.select 会立即通知可读性。此 “peek” 仅发生在 IO 对象上。对于 IO-like 对象(例如 OpenSSL::SSL::SSLSocket )不会发生这种情况。

使用 IO.select 的最佳方法是在 read_nonblock、write_nonblock 等非阻塞方法之后调用它。这些方法引发了一个由 IO::WaitReadable IO::WaitWritable 扩展的异常。模块通知调用者应该如何等待 IO.select 。如果引发 IO::WaitReadable ,调用者应该等待读取。如果引发 IO::WaitWritable ,调用者应该等待写入。

因此,可以使用 read_nonblock 和 IO.select 模拟阻塞读取(readpartial),如下所示:

begin
  result = io_like.read_nonblock(maxlen)
rescue IO::WaitReadable
  IO.select([io_like])
  retry
rescue IO::WaitWritable
  IO.select(nil, [io_like])
  retry
end

特别是,非阻塞方法和 IO.select 的组合对于 IO 类对象(如 OpenSSL::SSL::SSLSocket )是首选。它具有to_io 方法来返回底层 IO 对象。 IO.select 调用to_io获取要等待的文件说明符。

这意味着 IO.select 通知的可读性并不意味着 OpenSSL::SSL::SSLSocket 对象的可读性。

最可能的情况是 OpenSSL::SSL::SSLSocket 缓冲了一些数据。 IO.select 看不到缓冲区。因此,当 OpenSSL::SSL::SSLSocket#readpartial 不阻塞时, IO.select 可以阻塞。

但是,存在几种更复杂的情况。

SSL 是一种记录序列的协议。记录由多个字节组成。因此,SSL 的远程端发送部分记录, IO.select 通知可读性但 OpenSSL::SSL::SSLSocket 无法解密字节并且 OpenSSL::SSL::SSLSocket#readpartial 将阻塞。

此外,远程端可以请求 SSL 重新协商,这会强制本地 SSL 引擎写入一些数据。这意味着 OpenSSL::SSL::SSLSocket#readpartial 可能会调用 write 系统调用并且它可以阻塞。在这种情况下, OpenSSL::SSL::SSLSocket#read_nonblock 引发 IO::WaitWritable 而不是阻塞。因此,调用者应该等待准备好写,如上例所示。

当多个进程从一个流中读取时,非阻塞方法和 IO.select 的组合对于 tty、管道套接字套接字等流也很有用。

最后,Linux 内核开发人员不保证 select(2) 的可读性意味着即使对于单个进程也可以遵循 read(2) 的可读性。请参阅 GNU/Linux 系统上的 select(2) 手册。

IO#readpartial 之前调用 IO.select 可以正常工作。但是,这不是使用 IO.select 的最佳方式。

select(2) 通知的可写性不显示可写的字节数。 IO#write 方法阻塞,直到给定的整个字符串被写入。因此,在 IO.select 通知可写性后,IO#write(two or more bytes) 可以阻塞。需要 IO#write_nonblock 以避免阻塞。

可以使用 write_nonblock 和 IO.select 模拟阻塞写入(写入),如下所示: IO::WaitReadable 还应该在 OpenSSL::SSL::SSLSocket 中为 SSL 重新协商而拯救。

while 0 < string.bytesize
  begin
    written = io_like.write_nonblock(string)
  rescue IO::WaitReadable
    IO.select([io_like])
    retry
  rescue IO::WaitWritable
    IO.select(nil, [io_like])
    retry
  end
  string = string.byteslice(written..-1)
end

参数

read_array

等待直到准备好读取的 IO 对象数组

write_array

等待直到准备好写入的 IO 对象数组

error_array

等待异常的 IO 对象数组

暂停

以秒为单位的数值

示例

rp, wp = IO.pipe
mesg = "ping "
100.times {
  # IO.select follows IO#read.  Not the best way to use IO.select.
  rs, ws, = IO.select([rp], [wp])
  if r = rs[0]
    ret = r.read(5)
    print ret
    case ret
    when /ping/
      mesg = "pong\n"
    when /pong/
      mesg = "ping "
    end
  end
  if w = ws[0]
    w.write(mesg)
  end
}

产生:

ping pong
ping pong
ping pong
(snipped)
ping

相关用法


注:本文由纯净天空筛选整理自ruby-lang.org大神的英文原创作品 Kernel.select。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。