Как изящно завершить поток в Ruby
Я экспериментировал с концепцией многопоточности в Ruby в течение прошлой недели.
Для практики я разрабатываю загрузчик файлов, который выполняет параллельные запросы для набора URL-адресов. В настоящее время мне нужно безопасно завершить потоки при срабатывании сигнала прерывания. Я прочитал теорию многопоточности и перехвата сигнала во время выполнения. Тем не менее, несмотря на все эти теоретические знания, я до сих пор не знаю, как использовать их на практике.
В любом случае, я оставляю свою экспериментальную работу ниже.
class MultiThread
attr_reader :limit, :threads, :queue
def initialize(limit)
@limit = limit
@threads = []
@queue = Queue.new
end
def add(*args, &block)
queue << [block, args]
end
def invoke
1.upto(limit).each { threads << spawn_thread }
threads.each(&:join)
end
private
def spawn_thread
Thread.new do
Thread.handle_interrupt(RuntimeError => :on_blocking) do
# Nothing to do
end
until queue.empty?
block, args = queue.pop
block&.call(*args)
end
end
end
end
urls = %w[https://example.com]
thread = MultiThread.new(2)
urls.each do |url|
thread.add do
puts "Downloading #{url}..."
sleep 1
end
end
thread.invoke
1 ответ
Да, документы для
handle_interrupt
сбивают с толку. Попробуйте это, что я основал на
connection_pool
драгоценный камень, используемый, например,
puma
.
$stdout.sync = true
threads = 3.times.map { |i|
Thread.new {
Thread.handle_interrupt(Exception => :never) do
begin
Thread.handle_interrupt(Exception => :immediate) do
puts "Thread #{i} doing work"
sleep 1000
end
ensure
puts "Thread #{i} cleaning up"
end
end
}
}
Signal.trap("INT") {
puts 'Exiting gracefully'
threads.each { |t|
puts 'killing thread'
t.kill
}
exit
}
threads.each { |t| t.join }
Выход:
Thread 1 doing work
Thread 2 doing work
Thread 0 doing work
^CExiting gracefully
killing thread
killing thread
killing thread
Thread 0 cleaning up
Thread 1 cleaning up
Thread 2 cleaning up