我正在编写一个Ruby应用程序(Linux x86_64中的Ruby v2.1.3p242),它将重复处理在线数据并将结果存储在数据库中。为了加快速度,我有多个并发运行的线程,我一直在努力在命令和从线程引发异常时干净地停止所有线程。
问题是在调用#do_stuff
之后,某些线程将继续运行Sever.stop
的多次迭代。他们最终会停止,但我会看到一些线程在其余部分停止后运行10-50次。
每个帖子'在每次迭代之前锁定互斥锁并在之后解锁。调用@mutex.synchronize { kill }
时,代码Server.stop
在每个线程上运行。这个应该在下一次迭代后立即杀死该线程,但似乎并非如此。
修改
代码按原样运行,如果您愿意,可随意测试。在我的测试中,调用Server.stop
后所有线程停止需要30秒到几分钟。请注意,每次迭代需要1-3秒。我使用以下代码测试代码(在同一目录中使用ruby -I.
):
require 'benchmark'
require 'server'
s = Server.new
s.start
puts Benchmark.measure { s.stop }
以下是代码:
server.rb:
require 'server/fetcher_thread'
class Server
THREADS = 8
attr_reader :threads
def initialize
@threads = []
end
def start
create_threads
end
def stop
@threads.map {|t| Thread.new { t.stop } }.each(&:join)
@threads = []
end
private
def create_threads
THREADS.times do |i|
@threads << FetcherThread.new(number: i + 1)
end
end
end
服务器/ fetcher_thread.rb:
class Server
class FetcherThread < Thread
attr_reader :mutex
def initialize(opts = {})
@mutex = Mutex.new
@number = opts[:number] || 0
super do
loop do
@mutex.synchronize { do_stuff }
end
end
end
def stop
@mutex.synchronize { kill }
end
private
def do_stuff
debug "Sleeping for #{time_to_sleep = rand * 2 + 1} seconds"
sleep time_to_sleep
end
def debug(message)
$stderr.print "Thread ##{@number}: #{message}\n"
end
end
end
答案 0 :(得分:1)
无法保证调用stop
的线程在下一次循环迭代之前将获取互斥锁。这完全取决于Ruby和操作系统调度程序,有些操作系统(including Linux)没有实现FIFO调度算法,但还要考虑其他因素来尝试优化性能。
您可以通过避免kill
并使用变量干净地退出循环来使其更具可预测性。然后,您只需要在访问变量
class Server
class FetcherThread < Thread
attr_reader :mutex
def initialize(opts = {})
@mutex = Mutex.new
@number = opts[:number] || 0
super do
until stopped?
do_stuff
end
end
end
def stop
mutex.synchronize { @stop = true }
end
def stopped?
mutex.synchronize { @stop }
end
#...
end
end