Ruby,捕获库线程异常



我使用的是一个在后台线程中抛出异常的gem,如下所示。我想抓住这个异常,但不确定如何去做。如何处理库线程中的异常?

#this class is in my code
class MQTT
  def self.connect
      @client = Client.connect(options)
  end
ende

这个类在打包为gem的库中,所以我技术上不能访问它:

class Client
  def self.connect(*args, &block)
    client = Client.new(*args)
    client.connect(&block)
    return client
  end
  def connect(clientid=nil)
      # Start packet reading thread
      @read_thread = Thread.new(Thread.current) do |parent|
        Thread.current[:parent] = parent
        loop { receive_packet }
      end
  end
  def receive_packet
    begin
      # Poll socket - is there data waiting?
      result = IO.select([@socket], nil, nil, SELECT_TIMEOUT)
      # Pass exceptions up to parent thread
    rescue Exception => exp
      unless @socket.nil?
        @socket.close
        @socket = nil
      end
      Thread.current[:parent].raise(exp)
    end
  end
end

我想你有三个选择。

可以将异常返回给调用线程:

def receive_packet
  raise "Exception in #{Thread.current}"
rescue Exception => exp
  return exp
end
t1 = Thread.new do
  receive_packet
end
puts "t1: #{t1.value.inspect}"

你可以在加入线程时捕获异常(注意,你可以在这里修改或使用一个ensure块来确保你的套接字已关闭):

def receive_packet
  raise "Exception in #{Thread.current}"
rescue Exception => exp
  # reraise the exception
  raise exp
end
t = Thread.new do
  receive_packet
end
begin
  t.join
rescue => e
  puts "Exception caught from joined thread #{e.message} "
end

或者设置#abort_on_exception = true,使异常杀死所有线程:

Thread.abort_on_exception = true
begin
  Thread.new do
    receive_packet
  end
  sleep 1
rescue => e
  puts "Exception raised immediately to main thread: #{e.message}"
end

更新根据你上面的和你的评论,我猜你需要等待调用receive_packet的线程完成。所以你必须加入他们:

class Client
  def self.connect(*args, &block)
    client = Client.new(*args)
    client.connect(&block)
    return client
  end
  def initialize(args)
    @count = 0
  end
  def connect(clientid=nil)
    puts "Connecting. Thread.current is #{Thread.current}"
    # Start packet reading thread
    @read_thread = Thread.new(Thread.current) do |parent|
      Thread.current[:parent] = parent
      loop { receive_packet }
    end
  end
  def receive_packet
    begin
      # Poll socket - is there data waiting?
      # result = IO.select([@socket], nil, nil, SELECT_TIMEOUT)
      sleep 0.1
      @count += 1
      puts "count is now #{@count}"
      if @count == 3
        raise "WOOT: #{@count}"
      end
      # Pass exceptions up to parent thread
    rescue Exception => exp
      unless @socket.nil?
        @socket.close
        @socket = nil
      end
      puts "Reraising error #{exp.inspect} from #{Thread.current} to #{Thread.current[:parent]}"
      Thread.current[:parent].raise(exp)
    end
  end
end
class MQTT
  def self.connect
    @client = Client.connect(options = {})
  end
end
begin
  MQTT.connect
  Thread.list.each do |t|
    # Wait for the thread to finish if it isn't this thread (i.e. the main thread).
    t.join if t != Thread.current
  end
rescue => e
  puts "Exception from child thread: #{e.inspect}"
end

最新更新