前几天我遇到了一个问题,我花了 2 个小时在错误的地方寻找答案。
在此过程中,我将代码精简为以下版本。只要我有 sleep(0.1)
,这里的线程就可以工作在创建线程的循环中。
如果省略该行,则会创建所有线程 - 但实际上只有线程 7 会消耗队列中的数据。
有了这个“黑客”,我确实有一个可行的解决方案,但不是我满意的解决方案。我真的很好奇为什么会这样。
我在 windows 2.4.1p111 下使用了相当旧版本的 ruby。但是,我能够使用新的 ruby 3.0.2p107 安装重现相同的行为
#!/usr/bin/env ruby
@q = Queue.new
# Get all projects (would be a list of directories)
projects = [*0..100]
projects.each do |project|
@q.push project
end
def worker(num)
while not @q.empty?
puts "Thread: #{num} Project: #{@q.pop}"
sleep(0.5)
end
end
threads=[]
for i in 1..7 do
threads << Thread.new { worker(i) }
sleep(0.1) # Threading does not work without this line - but why?
end
threads.each {|thread| puts thread.join }
puts "done"
最佳答案
有趣的错误!这是一个竞争条件。
并不是只有线程 7 在工作,而是所有线程都在引用内存中的同一个变量 i(只有一个副本!)所以因为
数字 7 最后写入(推测在任何线程启动之前)它们都读取相同的 i==7
.
试试这个工作函数,看看它是否能解决问题
def worker(num)
my_thread_id = Thread.current.object_id
while not @q.empty?
puts "Thread: #{num} NumObjId: #{num.object_id} ThreadId: #{my_thread_id} Project: #{@q.pop}"
sleep(0.5)
end
end
请注意 NumObjId 在所有线程中都是相同的。他们都指向同一个数字。但是我们得到的实际 ThreadId 是不同的。如果您确实需要每个线程中的数字,则分配与线程一样多的数字。就像是
ids = (1..7).to_a
ids.each do |i|
threads << Thread.new { worker(i) }
end
关于如果线程生成速度过快,Ruby 工作分配会失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69522148/