两个版本的相同代码没有给出相同的结果

时间:2016-04-07 15:11:47

标签: ruby multithreading timer

我正在尝试实现一个处理不同请求超时的简单超时类。

这是第一个版本:

class MyTimer
  def handleTimeout mHash, k
    while mHash[k] > 0 do
        mHash[k] -=1
        sleep 1
        puts "#{k} : #{mHash[k]}"
    end
  end
end

MAX = 3

timeout = Hash.new
timeout[1] = 41
timeout[2] = 5
timeout[3] = 14

t1 = MyTimer.new 
t2 = MyTimer.new 
t3 = MyTimer.new 

first = Thread.new do
    t1.handleTimeout(timeout,1)
end

second = Thread.new do 
    t2.handleTimeout(timeout,2)
end

third = Thread.new do 
    t3.handleTimeout(timeout,3)
end

first.join
second.join
third.join

这似乎工作正常。所有超时都相互独立。 Screenshot attached

然而,代码的第二个版本会产生不同的结果:

class MyTimer
  def handleTimeout mHash, k
    while mHash[k] > 0 do
        mHash[k] -=1
        sleep 1
        puts "#{k} : #{mHash[k]}"
    end
  end
end

MAX = 3

timeout = Hash.new
timers = Array.new(MAX+1)
threads = Array.new(MAX+1)


for i in 0..MAX do
  timeout[i] = rand(40)
  # To see timeout value
  puts "#{i} : #{timeout[i]}"
end

sleep 1

for i in 0..MAX do
  timers[i] = MyTimer.new
  threads[i] = Thread.new do
    timers[i].handleTimeout( timeout, i)
  end
end


for i in 0..MAX do
  threads[i].join
end

Screenshot attached

为什么会这样?

如何使用数组实现此功能?

有没有更好的方法来实现相同的功能?

1 个答案:

答案 0 :(得分:0)

在使用Thread.new创建线程的循环中,变量imain线程(创建线程的位置)和创建的线程之间共享。因此,i看到的handleTimeout值并不一致,您会得到不同的结果。

您可以通过在方法中添加调试语句来验证这一点:

 #...
 def handleTimeout mHash, k
    puts "Handle timeout called for #{mHash} and #{k}"
    #...
 end
 #...

要解决此问题,您需要使用以下代码。这里将参数传递给Thread.new,然后使用块变量进行访问。

for i in 0..MAX do
  timers[i] = MyTimer.new
  threads[i] = Thread.new(timeout, i) do |a, b|
    timers[i].handleTimeout(a, b)
  end
end

When do you need to pass arguments to Thread.new?和此article中介绍了有关此问题的更多信息。