Ruby,捕获库线程异常?

时间:2014-03-26 02:26:47

标签: ruby exception-handling mqtt

我正在使用一个在背景线程中抛出异常的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

1 个答案:

答案 0 :(得分:1)

我认为你有3种选择。

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

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