鉴于此剪辑:
thr = []
for n in 0..6 do
thr[n] = Thread.new {
print n
}
end
for n in 0..6 do
thr[n].join
end
我期待的是:0123456
我得到了什么:2220000
目标是创建一个线程数组并等待每个线程完成。显然我对红宝石线程的理解在这里缺乏。
我该如何解决这个问题?
答案 0 :(得分:4)
不要将n
作为局部变量(具有词法范围)传递,而是将其作为块变量传递。
以下代码会将0
至6
的数字分别打印出来,但不保证订单。
thr = []
for n in 0..6 do
thr[n] = Thread.new(n) {|n|
print n
}
end
for n in 0..6 do
thr[n].join
end
您的代码无效的原因是因为块中的n
引用了n
中定义的for n in 0..6
,它在块执行期间发生了变化。并不总是得到2220000
,但在很多情况下,你会得到类似的结果。开始时2
的序列表示Thread.new(n)
创建新线程并继续执行print n
所花费的时间大约等于所需的时间。 for
循环执行两次迭代。
答案 1 :(得分:1)
其他答案可能会解决您的代码无效的原因。我将向自己说明如何获得您期望的结果。
线程不按顺序执行。如果你想要一系列线程的有序输出,你应该使用Mutex,这是目前没有为Ruby 2.3.0记录的。
下面,我提供同步和异步线程的示例,它们提供您正在寻找的有序输出。我还提供了一些关于线程安全的说明。
虽然我在下面提供了更完整的示例,但在保留原始代码风格的同时解决问题的最简单方法是:
for n in 0..6 do Thread.new { print n }.join end
通过在循环中使用#join,可以确保输出有序。这将正确打印以下标准输出:
0123456
以下是使用Mutex#synchronize的更惯用的示例:
def ordered_threads
result = ''
(0..6).each do |n|
Mutex.new.synchronize { result << n.to_s }
end
result
end
ordered_threads
#=> "0123456"
实际上不需要使用Thread#join因为所有线程都是由互斥锁顺序处理的。
如果您不想要使用#synchronize的开销,那么您可以随后订购输出。例如:
def unordered_threads
result, threads = [], []
(0..6).each do |n|
threads << Thread.new { result << n.to_s }
end
threads.each &:join
result.sort.join
end
unordered_threads
#=> "0123456"
此解决方案应解决您发布的用例,即使它在技术上不是线程安全的。如果您需要线程安全的解决方案,请使用Mutex或线程安全的对象,如thread_safe gem提供的对象。
答案 2 :(得分:1)
您可以使用tap
创建变量n
的副本,因此在循环中对其进行修改不会影响已创建的Thread
对象。
thr = []
for n in 0..6 do
n.tap do |n|
thr[n] = Thread.new {
print n
}
end
end
for n in 0..6 do
thr[n].join
end
#=> 0123456