我有以下代码(
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.
添加操作可能如下所示:
val = fetch_current(count1) add 1 to val store val back into count1
和count2类似的东西. Ruby可以在线程之间切换执行,因此它可能无法完成对变量的写入,但是当cpu返回到线程时,它应该从它被中断的行继续,对吧?
并且仍然只有一个线程正在写入变量.怎么可能在循环内部阻塞,count2 = 1执行的次数多么多?
解决方法
执行
puts "count1 : #{count1}"
需要一些时间(尽管可能很短).它不是在一个实例中完成的.因此,连续两条线并不神秘:
puts "count1 : #{count1}" puts "count2 : #{count2}"
正在显示不同的数量.简单地说,计数器线程经历了一些循环周期并在执行第一次执行时递增计数.
同样,何时
difference += (count1 - count2).abs
计算后,计数可以原则上递增,而count1在引用count2之前被引用.但是在该时间跨度内没有执行命令,我猜测引用count1所花费的时间比计数器线程通过另一个循环所花费的时间要短得多.请注意,前者中的操作是后者中所做操作的适当子集.如果差异足够大,这意味着在参数调用 – 方法期间计数器线程没有经历循环周期,则count1和count2将显示为相同的值.
预测将是,如果您在引用count1之后但在引用count2之前进行了一些昂贵的计算,那么差异将显示:
difference += (count1.tap{some_expensive_calculation} - count2).abs # => larger `difference`