2016-04-16 5 views
0

私は、あまりにも多くの時間を費やしているタスクの並列性を達成するためにマルチスレッドコードを書こうとしています。以下はその外観です:コード内のスレッドの安全性を確認

class A 
    attr_reader :mutex, :logger 
    def initialize 
    @reciever = ZeroMQ::Queue 
    @sender = ZeroMQ::Queue 
    @mutex = Mutex.new 
    @logger = Logger.new('log/test.log') 
    end 

    def run 
    50.times do 
     Thread.new do 
     run_parallel(@reciever.get_data) 
     end 
    end 
    end 

    def run_parallel(data) 
    ## Define some local variables. 
    a , b = data 
    ## Log some data to file. 
    logger.info "Got #{a}" 
    output = B.get_data(b) 
    ## Send output back to zermoq. 
    mutex.synchronize { @sender.send_data(output} } 
    end 
end 

コードがスレッドセーフであることを確認する必要があります。スレッド間でデータを共有したり変更すると(適切なミューテックスがない場合は@@@$など)、スレッドの安全性の問題が発生する可能性があります。

データにメソッドを渡すと、スレッドの安全性の問題が発生するかどうかはわかりません。言い換えれば、@@@$をメソッド内に使用していない場合は、コード内のrun_parallelmutexにラップする必要がありますか?あるいは、与えられたmutexの定義は十分ですか?

mutex.synchronize { @sender.send_data(output} } 

答えて

1

あなたがスレッドコンテキストで実行しているときはいつでも、あなたはローカル変数ではありません何の(簡単なヒューリスティックのための)意識するようになってきました。私はあなたのコードでこれらの潜在的な問題を参照してください。

  • run_parallel(@reciever.get_data)get_dataスレッドセーフですか?あなたはsend_dataを同期していて、どちらもZeroMQ::Queueなので、私は推測していません。

  • output = B.get_data(b)この呼び出しはスレッドセーフですか? bから何かを引っ張っても問題ありませんが、Bに状態を使用するか、状態のあるものを呼び出すと問題が発生します。

  • logger.info "Got #{a}"@coreywardは、Loggerがスレッドセーフであることを示しているので、これは問題ではありません。ただputsの上にそれに固執してください、あなたの出力を壊すでしょう。

あなたが@sender.send_dataのために、ミューテックスの中にいるならば、あなたは@senderが別のスレッドで、あなたのコード内のどこにもアクセスされないと仮定し、安全です。もちろん、synchronizeを投げるほど、スレッドが互いにブロックされパフォーマンスが低下するため、設計を見つけるために必要なバランスが取れます。

コードを機能させるには、ローカル状態のみを使用し、副作用のないメソッドを作成してみてください。あなたの仕事が複雑になるにつれて、スレッドセーフなデータ構造と他のパターンを助けることができるconcurrent-rubyのようなライブラリがあります。

+1

「Logger」はスレッドセーフなので、出力は期待どおりになるはずです。 – coreyward

+0

優秀、ありがとう@coreyward。経験豊かなテストが必要な人は、 'Array.new(500){| i | Thread.new {1000x {| j | Rails.logger.info "#{i} - #{j}"}}}。each(&:join) '。 –

関連する問題