當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


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。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。