我已经通过FFI为共享库函数(第三方函数)编写了一个包装器。此共享库尝试与服务器建立连接。在连接建立期间,当服务器无法访问时,第三方功能等待3分钟。为了避免在调用rails时我曾尝试使用以下超时但不幸的是它没有用。
注意:当我使用终结者时,由它创建的附加进程正在变为失效进程。
我正在使用ruby enterprise version 1.8
答案 0 :(得分:2)
似乎通过FFI调用完全阻止Ruby的调度程序,不允许任何线程。这可能与Ruby的绿色线程有关。
以下示例说明了使用FFI时Ruby并发的行为:
require 'ffi'
module Sleep
extend FFI::Library
ffi_lib FFI::Library::LIBC
attach_function :sleep, [:uint], :void
end
thread = Thread.start do
count = 1
while count <= 10
puts count
count += 1
sleep 0.5
end
end
puts "FFI sleep"
Sleep.sleep 5 # Everything blocks, second thread is run after sleep
puts "Ruby sleep"
sleep 5 # Scheduling works, other thread runs simultaneously
thread.join if thread.alive?
解决这个问题的一种方法是分叉一个单独的进程来执行FFI调用,并在其上超时:
require 'ffi'
require 'timeout'
module Sleep
extend FFI::Library
ffi_lib FFI::Library::LIBC
attach_function :sleep, [:uint], :void
end
child_pid = Process.fork do
Signal.trap("INT") do
exit
end
Sleep.sleep 5
exit
end
begin
Timeout::timeout(2) do
Process.wait(child_pid)
end
rescue Timeout::Error
Process.kill("INT", child_pid)
end
在分叉的子进程中,我们感兴趣的是,正在监听INT
信号,如果达到超时则轻轻关闭,当然还要进行FFI呼叫。
在父进程中,我们只需要超时子进程,除非按时完成,否则将其终止。
答案 1 :(得分:1)
有点清洁:
require 'ffi'
module Sleep
extend FFI::Library
ffi_lib FFI::Library::LIBC
attach_function :sleep, [:uint], :void, :blocking => true
end
答案 2 :(得分:0)
您可以将在C库中阻止的功能标记为“阻止”功能,FFI将围绕对这些功能的调用解锁GIL。 (需要ffi-1.0.x)。
e.g。
require 'ffi'
module Sleep
extend FFI::Library
ffi_lib FFI::Library::LIBC
# Tell FFI that this function may block
@blocking = true
attach_function :sleep, [:uint], :void
end
@blocking不粘 - 您需要在每个要标记为阻止的'attach_function'调用之前设置它。
它不是100%肯定的解决方案。中断在本机代码中被阻塞的函数将适用于可中断的函数(例如,睡眠,读取,写入等),但不适用于某些本机代码(例如,cpu密集型计算,也可能是许多其他类型)。 / p>
警告:在ruby 1.8.x上,阻塞函数调用真的慢(与阻止1.9或JRuby上的调用相比)。