如果线程生成速度过快,Ruby 工作分配会失败

标签 ruby multithreading queue sleep worker

前几天我遇到了一个问题,我花了 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/

相关文章:

ruby-on-rails - “此锁文件必须使用Bundler 2或更高版本。”即使安装了Bundler 2.0.2

java - Java Lambdas线程安全

c++ - std::thread 并在 visual studio 2013 中 move

objective-c - 我们如何限制Objective-C中的线程数

redis - 使用Redis的MultiPublisher-MultiConsumer队列

python - Python 的通用优先级队列

ruby - 用于查看字符串是否包含某个范围内的数字的正则表达式模式

ruby - 如何在遍历数组时使用 Array#delete?

python - 检查 Google App Engine 中任务队列的状态

javascript - 使用 Watir Webdriver 在 Firefox 中打开新选项卡