我看到一篇文章,它为作者建议了以下代码:
output = open("my_pipe", "w+") # the w+ means we don't block
output.puts "hello world"
output.flush # do this when we're done writing data
和读者:
input = open("my_pipe", "r+") # the r+ means we don't block
puts input.gets # will block if there's nothing in the pipe
但是open
,puts
,gets
会阻止该程序吗?是否有某种超时?可以改变吗?另外,为什么w+
意味着非阻塞呼叫?它转换为哪个open
系统调用标志?
好的,让我和你分享我对世界的描绘。正如rogerdpack
所说,有两种选择:
select
,2O_NONBLOCK
标志,read_nonblock
,write_nonblock
,select
方法)。我没有尝试过,所以这些只是猜测。
至于为什么open
,puts
和gets
可能会阻塞线程。 open
调用块,直到至少有一个读取器和至少一个写入器。这一定是我们需要指定的原因 r+
,w+
用于open
调用。从strace
输出来看,它们都转换为O_RDWR
标志。然后必须有一些缓冲区,其中存储了尚未接收的数据。这一定是编写方法可能会阻塞的原因。读取方法可能会阻塞,因为它们期望有更多的数据可用,而不是实际数据。
UPD
如果一个进程试图从空管道中读取, 则 read(2) 将阻塞,直到数据可用。如果一个进程试图写入一个完整的管道(见下文),则 write(2) 阻塞,直到从管道中读取足够的数据以允许写入完成。
-- http://linux.die.net/man/7/pipe
必须先在两端(读取和写入)打开 FIFO,然后才能传递数据。通常,打开FIFO块,直到另一端也打开。
在 Linux 下,打开 FIFO 进行读取和写入将在阻塞和非阻塞模式下成功。POSIX 未定义此行为。这可用于在没有阅读器可用的情况下打开 FIFO 进行写入。
-- http://linux.die.net/man/7/fifo
这是我想出的实现:
#!/home/yuri/.rbenv/shims/ruby
require 'timeout'
data = ((0..15).to_a.map { |v|
(v < 10 ? '0'.ord + v : 'a'.ord + v - 10).chr
} * 4096 * 2).reduce('', :+)
timeout = 10
start = Time.now
open('1.fifo', File::WRONLY | File::NONBLOCK) { |out|
out.flock(File::LOCK_EX)
nwritten = 0
data_len = data.length
begin
delta = out.write_nonblock data
data = data[delta..-1]
nwritten += delta
rescue IO::WaitWritable, Errno::EINTR
timeout_left = timeout - (Time.now - start)
if timeout_left < 0
puts Time.now - start
raise Timeout::Error
end
IO.select nil, [out], nil, timeout_left
retry
end while nwritten < data_len
}
puts Time.now - start
但是对于我手头的问题,我决定忽略这个超时的事情。当管道的另一端没有读取器时,它可能就足够了(Errno::ENXIO):
open('1.fifo', File::WRONLY | File::NONBLOCK) { |out|
out.flock(File::LOCK_EX)
nwritten = 0
data_len = data.length
begin
delta = out.write_nonblock data
data = data[delta..-1]
nwritten += delta
rescue IO::WaitWritable, Errno::EINTR
IO.select nil, [out]
retry
end while nwritten < data_len
}
附言感谢您的反馈。
这个页面应该回答你所有的问题...http://www.ruby-doc.org/core-2.0.0/IO.html
通常,put 总是会阻塞当前线程,因为它们可能需要等待 IO 完成才能返回。 get 还可以阻止当前线程,因为它将永远读取并读取,直到它到达第一个换行符,然后它将返回它读取的所有内容。 呵呵。