【问题标题】:Loops in multiple threads多线程循环
【发布时间】:2012-10-25 07:58:27
【问题描述】:

我有以下代码 (from a Ruby tutorial):

require 'thread'

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      count1 += 1
      count2 += 1
   end
end
spy = Thread.new do
   loop do
      difference += (count1 - count2).abs
   end
end
sleep 1

puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"
counter.join(2)
spy.join(2)
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

这是使用Mutex.synchronize 的示例。在我的电脑上,结果与教程完全不同。调用join 后,有时计数相等:

count1 :  5321211
count2 :  6812638
difference : 0
count1 :  27307724
count2 :  27307724
difference : 0

有时不是:

count1 :  4456390
count2 :  5981589
difference : 0
count1 :  25887977
count2 :  28204117
difference : 0

我不明白为什么即使计数显示的数字非常不同,差异仍然是 0

add 操作可能如下所示:

val = fetch_current(count1)
add 1 to val
store val back into count1

count2 类似的东西。 Ruby 可以在线程之间切换执行,所以它可能不会完成对变量的写入,但是当 CPU 回到线程时,它应该从中断的那一行继续,对吧?

并且仍然只有一个线程正在写入变量。怎么可能,在loop do 块内,count2 += 1 被执行了更多次?

【问题讨论】:

  • join(2) 应该做什么?
  • 它给线程一个终止的限制(以秒为单位)。如果我不调用它,ruby 将在程序结束时自动获取线程(因此无限 loop do 将始终结束)。请参阅ruby-doc.org/core-1.9.3/Thread.html#method-i-join 了解更多信息
  • 这很有趣。在 ruby​​ 1.8 上,difference 始终为 0,并且计数的差异永远不会超过 1,但在 ruby​​ 1.9 上,difference 始终 == 0,但 count1 和 count2 彼此相距很远。

标签: ruby multithreading ruby-1.9


【解决方案1】:

执行

puts "count1 :  #{count1}"

需要一些时间(尽管可能很短)。它不是在一个实例中完成的。因此,连续的两行并不神秘:

puts "count1 :  #{count1}"
puts "count2 :  #{count2}"

显示不同的计数。简单地说,counter 线程在执行第一个 puts 时经历了一些循环周期并增加了计数。

同样,当

difference += (count1 - count2).abs

被计算,原则上计数可能会增加,而 count1 在引用 count2 之前被引用。但是在那个时间跨度内没有执行任何命令,而且我猜想引用count1 所需的时间比counter 线程通过另一个循环所需的时间要短得多。请注意,前者完成的操作是后者完成的操作的适当子集。如果差异足够显着,这意味着counter 线程在- 方法的参数调用期间没有经过循环循环,那么count1count2 将显示为相同的值。

一个预测是,如果你在引用count1 之后但在引用count2 之前进行一些昂贵的计算,那么difference 将会出现:

difference += (count1.tap{some_expensive_calculation} - count2).abs
# => larger `difference`

【讨论】:

  • 事实上,将sleep 0.001 之类的东西放在计数器之间的计数器线程中也会显示出差异。由于计数器线程“忙于循环”,我想知道间谍线程是否有很多机会在 1.9 中运行。立即将 sleep 语句放入可以释放 CPU 时间来运行 spy 线程,差异就显现出来了。
  • 谢谢,有道理。用print "count1 : #{count1}, count2 : #{count2}\n" 替换puts 可以减少计数器之间的差异。因为puts 作为两个命令执行,因此需要更多时间。
【解决方案2】:

这就是答案。我认为您已经假设线程在join(2) 返回后停止执行。

事实并非如此!即使join(2) 将执行(暂时)返回给主线程,线程也会继续运行。

如果您将代码更改为此,您将看到会发生什么:

...
counter.join(2)
spy.join(2)

counter.kill
spy.kill

puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

这在 ruby​​ 1.8 中似乎有点不同,在主线程执行时线程似乎没有机会运行。

本教程可能是为 ruby​​ 1.8 编写的,但是线程模型从那时起在 1.9 中发生了变化。

事实上,它在 1.8 中工作纯粹是“运气”,因为当 join(2) 在 1.8 和 1.9 中都没有返回时,线程没有完成执行。

【讨论】:

    猜你喜欢
    • 2013-12-24
    • 1970-01-01
    • 1970-01-01
    • 2015-03-26
    • 2010-09-18
    • 2011-08-20
    • 2011-02-13
    • 1970-01-01
    • 2018-08-07
    相关资源
    最近更新 更多