注:DNS名を解決するためにスレッドを使用することを選択していますが、同じ動作がどのタイプの同様の操作でも再現される可能性があります。
(以前は機能していた)コードを標準のシングルスレッド実行からマルチスレッドに移動しようとすると、予期しない結果が発生します。具体的には、私のコードはハッシュの配列を反復処理し、配列内の各ハッシュにキーと値のペアを追加します。
私が抱えている問題dns_cname.map
は、新しいキーと値のペアが作成されているループに起因しているようです。"external_dns_entry"
正しい値(つまり、result.name.to_s
DNSによって解決された名前を含む)を持つキーの代わりに、代わりに他の多くのサーバーの1つの名前を取得していますurl_nameserver_mapping
。
スレッドが利用可能になり、ハッシュが順不同に更新されると、DNS解決が行われているように感じますが、このような問題の追跡を開始する方法すらわかりません。
問題のある結果:server1に対して実行されたDNS解決はサーバー17にマッピングされています。同様に、サーバー17はサーバー99などにマッピングされています。残りのハッシュはそのままです。
どんな助けでも大歓迎です。よろしくお願いします!
マルチスレッドが有効になっていない場合の私のコードは次のとおりです(正しく機能します)。
url_nameserver_mapping = { "server1" => "dallasdns.dns.com",
"server2" => "portlanddns.dns.com",
"server3" => "losangelesdns.dns.com" }
# Parse the JSON string response from the API into a valid Ruby Hash
# The net/http GET request is not shown here for brevity but it was stored in 'response'
unsorted_urls = JSON.parse(response.body)
# Sort (not sure this is relevant)
# I left it since my data is being populated to the Hash incorrectly (w/ threading enabled)
url_properties = unsorted_urls['hostnames']['items'].sort_by { |k| k["server"]}
url_nameserver_mapping.each do |server,location|
dns = Resolv::DNS.new(:nameserver => ['8.8.8.8'])
dns_cname = dns.getresources(server, Resolv::DNS::Resource::IN::CNAME)
dns_cname.map do |result|
# Create a new key/value for each Hash in url_properties Array
# Occurs if the server compared matches the value of url['server'] key
url_properties.each do |url|
url["external_dns_entry"] = result.name.to_s if url['server'] == server
end
end
end
https://blog.engineyard.cm/2013/ruby-concurrencyのガイドに従って、プロデューサー/コンシューマースレッドモデルを実装しました。
マルチスレッドが有効になっている(機能していない)場合の適応コードは次のとおりです。
require 'thread'
require 'monitor'
thread_count = 8
threads = Array.new(thread_count)
producer_queue = SizedQueue.new(thread_count)
threads.extend(MonitorMixin)
threads_available = threads.new_cond
sysexit = false
url_nameserver_mapping = { "server1" => "dallasdns.dns.com",
"server2" => "portlanddns.dns.com",
"server3" => "losangelesdns.dns.com" }
unsorted_urls = JSON.parse(response.body)
url_properties = unsorted_urls['hostnames']['items'].sort_by { |k| k["server"]}
####################
##### Consumer #####
####################
consumer_thread = Thread.new do
loop do
break if sysexit && producer_queue.length == 0
found_index = nil
threads.synchronize do
threads_available.wait_while do
threads.select { |thread| thread.nil? ||
thread.status == false ||
thread["finished"].nil? == false}.length == 0
end
# Get the index of the available thread
found_index = threads.rindex { |thread| thread.nil? ||
thread.status == false ||
thread["finished"].nil? == false }
end
@domain = producer_queue.pop
threads[found_index] = Thread.new(@domain) do
dns = Resolv::DNS.new(:nameserver => ['8.8.8.8'])
dns_cname = dns.getresources(@domain, Resolv::DNS::Resource::IN::CNAME)
dns_cname.map do |result|
url_properties.each do |url|
url["external_dns_entry"] = result.name.to_s if url['server'] == @domain
end
end
Thread.current["finished"] = true
# Notify the consumer that another batch of work has been completed
threads.synchronize { threads_available.signal }
end
end
end
####################
##### Producer #####
####################
producer_thread = Thread.new do
url_nameserver_mapping.each do |server,location|
producer_queue << server
threads.synchronize do
threads_available.signal
end
end
sysexit = true
end
# Join on both the producer and consumer threads so the main thread doesn't exit
producer_thread.join
consumer_thread.join
# Join on the child processes to allow them to finish
threads.each do |thread|
thread.join unless thread.nil?
end
@domain
はすべてのスレッドで共有されます-この共有が問題の根本です。キューから次の作業単位をポップして更新すると、すべてのスレッドでその変更が確認されます。あなたはすることによってこの問題を回避することができます
Thread.new(producer_queue.pop) do |domain|
#domain isn't shared with anyone (as long as there
#is no local variable called domain in the enclosing scope
end
あなたの質問に正接しますが、これは本当に過剰に設計されたアプローチが好きだったようです。多数のコンシューマースレッドを事前にスピンアップして、作業キューから直接読み取らせる方がはるかに簡単です。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加