我使用的是一个在后台线程中抛出异常的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