2016-09-15 51 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