はじめに: 以下のサンプルコードについて、少し議論します。議論は、下のコードにスレッドの問題が存在するかどうかです。私たちが探しているのは、なぜそれが存在するのか、なぜ存在しないのかに対する良い答えです。Rubyスレッドの問題またはスレッドの問題はありませんか?
以下の例は、以下を示しています。ネットワークコールを表すIoBoundApiCall
という名前のクラスが構築されます。このクラスは、何らかの理由でディスカッションに関連しない限り、無視する必要があります。そうであれば、無関係にすることをお手伝いします。 Googleのプロダクションコードでは、これはGoogle APIへのクエリです。それに続いて、1000個のアイテムの配列を設定するループがあり、配列の各アイテムはハッシュです。これにより、「共有データ」が設定されます。
次に、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
注:Ruby 2.1 MRIを使用しています – GrokSrc