2016-09-15 36 views
0

前言: 所以我们有一点的讨论正在进行关于下面的示例代码。争论在于下面的代码中是否存在线程问题。我们正在寻找的是为什么它存在或为什么它不存在的一个很好的答案。Ruby线程问题还是无线程问题?

下面的例子显示如下。一个类构建为名为IoBoundApiCall,表示网络调用。除非与讨论有关,否则应该忽略这个类,如果这样有助于使它无关紧要,在我们的产品代码中,这是对Google API的查询。接下来是一个循环,它建立一个包含一千个项目的数组,数组中的每个项目都是一个散列。这设置了“共享数据”。

接下来我们有问题的代码,在100每批产卵的100个线程,使得伪API调用组分批循环,并将结果保存回哈希。循环的结果输出到yaml进行检查。请注意,不使用互斥锁。

程序的输出:正确的程序输出如下所示。

--- 
- :id: '1' 
    :data: string1 
    :results: 
    - '0': id local_string1 slept for 1 
    - '1': id local_string1 slept for 1_copy 
- :id: '2' 
    :data: string2 
    :results: 
    - '0': id local_string2 slept for 0 
    - '1': id local_string2 slept for 0_copy 
. 
. 
. 

线程问题输出: Unexepcted输出看起来像下面这样。请注意,string1结果不正确地string2

--- 
- :id: '1' 
    :data: string1 
    :results: 
    - '0': id local_string2 slept for 0 
    - '1': id local_string2 slept for 0_copy 
- :id: '2' 
    :data: string2 
    :results: 
    - '0': id local_string1 slept for 1 
    - '1': id local_string1 slept for 1_copy 
. 
. 
. 

问题配对:在下面的代码是有可能存在的竞争条件,其中结果被存储了错误的哈希?为什么或者为什么不。

#!/usr/bin/env ruby 
require 'bundler' 
require 'yaml' 

Bundler.require 

# What this code is doesn't really matter. It's a network bound API service call. 
# It's only here to make the example below work. Please ignore this class 
class IoBoundApiCall 
    def query(input) 
    randomly = rand(0.0..1.0) 
    sleep randomly 
    ["id #{input} slept for #{randomly}", "id #{input} slept for #{randomly}_copy"] 
    end 
end 

api = IoBoundApiCall.new 

inputs = [] 

(1..1000).each do |i| 
    inputs << { 
    id: "#{i}", 
    data: "string#{i}", 
    results: [] 
    } 
end 

# This is the code in question 
inputs.each_slice(100) do |batch| 
    threads = [] 
    batch.each do |input| 
    threads << Thread.new do 
     data_from_hash = input[:data] 
     thread_local_string = "local_#{data_from_hash}" 

     questionable_results = api.query(thread_local_string) 
     questionable_results.each_with_index do |questionable_result, i| 
     result = {} 
     result["#{i}"] = questionable_result 

     # DANGER WILL ROBINSON!! THREADING ISSUE?? 
     input[:results] << result 
     end 
    end 
    end 
    threads.map(&:join) 
end 

puts inputs.to_yaml 
+0

注意:我们正在使用Ruby 2.1 MRI – GrokSrc

回答

1

有了官方的Ruby VM(YARV),没有线程问题。 YARV是完全线程不安全的,所以基本上每次触摸一个Ruby对象时,全局VM锁(GVL)会阻塞除一个线程之外的所有线程,以防止由于多个线程彼此跳步而导致对象进入无效状态。

该代码可引起问题的唯一方法是,如果更新的输入物体引起虚拟机的内部状态,其与另一个线程同时被更新一个不同的输入冲突一些副作用。但这正是GVL所预防的。

+0

GVL只让ruby的本地C函数线程安全! 否则在Ruby中不需要互斥锁。 –

+0

在这种情况下唯一潜在的种族涉及共享输入对象,它只能通过...本地C函数修改! – Max