我已经通过 FFI 为共享库函数(第三方函数)编写了一个包装器。此共享库尝试与服务器建立连接。在连接建立期间,当服务器无法访问时,第三方功能等待 3 分钟。为了避免在调用 rails 时出现这种情况,我曾经尝试使用以下超时,但不幸的是它不起作用。
- 本机超时
- 系统超时
- 终结者
注意:当我使用终结者时,由它创建的附加进程变成了已失效的进程。
我正在使用 ruby 企业版 1.8
我已经通过 FFI 为共享库函数(第三方函数)编写了一个包装器。此共享库尝试与服务器建立连接。在连接建立期间,当服务器无法访问时,第三方功能等待 3 分钟。为了避免在调用 rails 时出现这种情况,我曾经尝试使用以下超时,但不幸的是它不起作用。
注意:当我使用终结者时,由它创建的附加进程变成了已失效的进程。
我正在使用 ruby 企业版 1.8
似乎通过 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 调用。
在父进程中,我们只需要让子进程超时,并杀死它,除非它按时完成。
干净一点:
require 'ffi'
module Sleep
extend FFI::Library
ffi_lib FFI::Library::LIBC
attach_function :sleep, [:uint], :void, :blocking => true
end
您可以将 C 库中将阻塞的函数标记为“阻塞”函数,FFI 将围绕对这些函数的调用解锁 GIL。(需要 ffi-1.0.x)。
例如
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 密集型计算,可能还有许多其他类型)。
请注意:在 ruby 1.8.x 上,阻塞函数调用非常慢(与 1.9 或 JRuby 上的阻塞调用相比)。