1

我已经通过 FFI 为共享库函数(第三方函数)编写了一个包装器。此共享库尝试与服务器建立连接。在连接建立期间,当服务器无法访问时,第三方功能等待 3 分钟。为了避免在调用 rails 时出现这种情况,我曾经尝试使用以下超时,但不幸的是它不起作用。

  1. 本机超时
  2. 系统超时
  3. 终结者

注意:当我使用终结者时,由它创建的附加进程变成了已失效的进程。

我正在使用 ruby​​ 企业版 1.8

4

3 回答 3

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 调用。

在父进程中,我们只需要让子进程超时,并杀死它,除非它按时完成。

于 2011-03-12T14:38:03.270 回答
1

干净一点:

require 'ffi'

module Sleep
  extend FFI::Library

  ffi_lib FFI::Library::LIBC

  attach_function :sleep, [:uint], :void, :blocking => true
end
于 2012-01-12T22:38:08.993 回答
0

您可以将 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 上的阻塞调用相比)。

于 2011-03-15T00:12:39.077 回答