2014-03-26 44 views
0

我正在使用一個在後臺線程中拋出異常的gem,如下所示。我想趕上這個例外,但不知道如何去做。如何去處理庫線程中的異常?Ruby,捕獲庫線程異常?

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

這個類是被包裝作爲寶石庫,所以我在技術上沒有訪問到它:

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

我覺得你有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}" 

你可以捕獲該異常上加入線程(注意,您可以在這裏再次加註或使用確保塊,以確保您的插座已關閉):

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 
+0

謝謝!所有這些解決方案都很有用,但啓動新線程的代碼位於gem內。我可以在不修改庫源的情況下使用其中的任何一種嗎? – WindsurferOak