将参数传递给红宝石线

时间:2016-01-11 16:24:36

标签: ruby multithreading

鉴于此剪辑:

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

目标是创建一个线程数组并等待每个线程完成。显然我对红宝石线程的理解在这里缺乏。

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:4)

不要将n作为局部变量(具有词法范围)传递,而是将其作为块变量传递。

以下代码会将06的数字分别打印出来,但不保证订单。

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)

TL; DR

其他答案可能会解决您的代码无效的原因。我将向自己说明如何获得您期望的结果。

线程不按顺序执行。如果你想要一系列线程的有序输出,你应该使用Mutex,这是目前没有为Ruby 2.3.0记录的。

下面,我提供同步和异步线程的示例,它们提供您正在寻找的有序输出。我还提供了一些关于线程安全的说明。

可能最简单的事情

虽然我在下面提供了更完整的示例,但在保留原始代码风格的同时解决问题的最简单方法是:

for n in 0..6 do Thread.new { print n }.join end

通过在循环中使用#join,可以确保输出有序。这将正确打印以下标准输出:

  

0123456

使用Mutex订购线程

以下是使用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