0

我正在写入线程服务器中的套接字(目前在 MRI 上运行)。使用以下代码执行此操作:

begin
  num_bytes_written = socket.write_nonblock(chunk)
  if num_bytes_written < chunk.bytesize
    chunk = chunk[num_bytes_written..-1]
    raise Errno::EINTR
  end
rescue IO::WaitWritable, Errno::EINTR
  Thread.pass if server_is_threaded
  IO.select(nil, [socket])
  retry
rescue Errno::EPIPE
  return
end

它的要点是,如果我有一个 WaitWritable (套接字已饱和),我希望运行它的服务器抢占另一个线程。这是Thread.pass一个好主意,或者如果我的线程正在执行 MRI 会自动抢占其他东西select()吗?

4

1 回答 1

1

线程始终运行,直到它超过其时间片或进入等待状态。在支持线程优先级和抢占的系统上,较高优先级的线程也可以抢占具有较低优先级的正在运行的线程,但这取决于系统。进入等待状态意味着线程正在等待某个事件发生,并且在该事件发生之前无法运行,例如 I/O 事件或定时事件,如休眠一段时间。

Thread.pass是大多数其他编程语言和操作系统所称的 yield,但当然,由于显而易见的原因,在 Ruby 中使用该术语会非常误导。这意味着您告诉操作系统将您的线程视为超出了它的时间片,让其他线程有机会立即运行,而不必等待您的线程用完它的所有时间片、等待或等待抢占。由于您的线程在屈服时保持在可运行状态(它不被视为等待任何东西),它可以立即再次运行,因此并不是说另一个线程将接管,因为线程调度程序可能认为您的线程仍然是决定下一个运行哪个线程时的最佳候选者(特别是如果所有其他可运行的线程具有较低的优先级)。

但是如果IO.select必须阻塞,这将使您的线程进入等待状态,然后它将不再可用于运行,因此在这种情况下,线程调度程序无论如何都必须选择另一个线程,这已记录在案:

调用 select(2) 系统调用。它监视给定的 IO 对象数组,等待一个或多个 IO 对象准备好读取、准备好写入并分别有未决异常,并返回一个包含这些 IO 对象数组的数组。

等待总是意味着线程停止运行,并且每当线程停止运行时,其他一些可运行的线程将接管。

但是,如果您的意图是总是让另一个线程有机会在发生错误的情况下运行,无论是否select会阻塞(因为它不必阻塞,直到select调用点,套接字可能是可写的然后它不会阻塞),那么调用Thread.pass. 然而,大多数程序员可能不会在那种情况下调用它,就好像select不会阻塞一样,您通常希望立即再次重复写入操作(在同一时间片内,如果可能,如果没有,您的线程无论如何都会通过)。

于 2019-04-21T01:02:37.823 回答