Ruby线程和变量

时间:2014-07-11 21:55:42

标签: ruby multithreading thread-safety

为什么结果不是1到10,而只是10s?

require 'thread'

def run(i)
  puts i
end

while true
  for i in 0..10
    Thread.new{ run(i)}
  end
  sleep(100)
end

结果:

10
10
10
10
10
10
10
10
10
10
10

为什么要循环?我正在循环运行,因为后来我想一直遍历数据库表并回显从数据库中检索到的任何记录。

3 个答案:

答案 0 :(得分:8)

传递给Thread.new的块实际上可能在将来某个时间点开始,到那时i的值可能已经改变。在您的情况下,在所有线程实际运行之前,它们都增加到10

要解决此问题,除了块之外,还要使用接受参数的Thread.new形式:

require 'thread'

def run(i)
  puts i
end

while true
  for i in 0..10
    Thread.new(i) { |j| run(j) }
  end
  sleep(100)
end

这会在调用j时将块变量i设置为new的值。

答案 1 :(得分:2)

@DavidGrayson是对的。

您可以在此处看到for loop中的副作用。在您的情况下,i变量范围是您的整个文件。虽然您希望for loop中只有一个区块作为范围。实际上这在惯用Ruby中是错误的方法。 Ruby为这项工作提供了迭代器。

(1..10).each do |i|
   Thread.new{ run(i)}
end

在这种情况下,变量i的范围将在块范围内被隔离,对于每次迭代,您将获得新的本地(对于此块)变量i

答案 2 :(得分:1)

问题是你创建了11个线程,它们都试图访问由程序主线程定义的同一个变量i。避免这种情况的一个技巧是在方法中调用Thread.new;那么线程有权访问的变量i就是传递给方法的特定i,并且不与其他线程共享。这利用了closure

require 'thread'

def run(i)
  puts i
end

def start_thread(i)
  Thread.new { run i }
end

for i in 0..10
  start_thread i
  sleep 0.1
end

结果:

0
1
2
3
4
5
6
7
8
9
10

(我添加了sleep只是为了保证线程以数字顺序运行,所以我们可以得到整齐的输出,但你可以把它拿出来并且仍然有一个有效的程序,其中每个线程都获得正确的参数。)