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

Posted

技术标签:

【中文标题】如果线程生成速度过快,Ruby 工作分配会失败【英文标题】:Ruby work distribution fails if threads are generated to fast 【发布时间】:2021-11-29 23:09:23 【问题描述】:

前几天我遇到了一个问题,我花了 2 个小时在错误的地方寻找答案。

在此过程中,我将代码精简为以下版本。只要我在创建线程的循环中有sleep(0.1),这里的线程就可以工作。

如果省略该行,则创建所有线程 - 但只有线程 7 会实际使用队列中的数据。

有了这个“hack”,我确实有一个可行的解决方案,但我并不满意。我真的很好奇为什么会这样。

我在 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"

【问题讨论】:

【参考方案1】:

有趣的错误!这是一个竞争条件。

并不是只有线程 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

【讨论】:

你说得对!在我的情况下,这并没有真正伤害,因为我只使用变量作为线程的标识符。然而,在其他情况下,这可能会产生很大的问题。 @mauam 我添加了一些解释,说明如果您愿意,您可以如何传递号码。 谢谢。非常感激!鉴于“问题”,我认为这是一个直截了当的解决方案。我确实需要这些线程中的数字,我显然更喜欢上面的示例,而不是我在原始代码中使用的 sleep @mauam 您也可以将for i in 1..7 do 更改为(1..7).each do |i|。 A for loop doesn’t create a new scope,所以 i 变量对于每次迭代都是相同的,但 each 确实创建了一个新范围,因此每次迭代 i var 是不同的。

以上是关于如果线程生成速度过快,Ruby 工作分配会失败的主要内容,如果未能解决你的问题,请参考以下文章

QT 信号量QSemaphore的使用

使用 OpenCV 和 PyQT5 视频播放速度过快

Azure 服务总线向队列中添加消息的速度过快

Token验证失败是怎么回事

小功能⭐️Unity解决物体移动速度过快不能检测到碰撞

Python 中 如何解决pip下载速度过慢