2016-04-16 12 views
0

Ich versuche, einen Multi-Threaded-Code zu schreiben, um Parallelität für eine Aufgabe zu erreichen, die zu viel Zeit in Anspruch nimmt. Hier ist, wie es aussieht:Stellen Sie Thread-Sicherheit im Code

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 

Man muss sicherstellen, dass der Code Thread sicher ist. Teilen und Ändern von Daten (wie @, @@, $ ohne ordnungsgemäße Mutex) über Threads könnte zu Thread Sicherheitsproblem führen.

Ich bin mir nicht sicher, ob, wenn ich die Daten an eine Methode übergebe, die Threadsicherheitsproblem ebenso ergibt. Mit anderen Worten, muss ich sicherstellen, dass der Teil meines Codes innerhalb run_parallel in eine mutex gewickelt werden muss, wenn ich keine @, @@, $ innerhalb der Methode verwende? Oder ist die angegebene Mutex-Definition ausreichend?

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

Antwort

1

Jedes Mal, wenn Sie in einem Gewinde Kontext laufen lassen, haben Sie sich bewusst sein (für eine einfache Heuristik) von etwas, das nicht eine lokale Variable ist. Ich sehe diese möglichen Probleme im Code:

  • run_parallel(@reciever.get_data) Ist get_data THREAD? Sie haben send_data synchronisiert, und sie sind beide ein ZeroMQ::Queue, also rate ich nicht.

  • output = B.get_data(b) Ist dieser Aufruf threadsafe? Wenn es nur etwas aus b herauszieht, geht es dir gut, aber wenn es in B Zustand verwendet oder irgendetwas anderes nennt, das Status hat, bist du in Schwierigkeiten.

  • logger.info "Got #{a}"@coreyward weist darauf hin, dass Logger ist THREAD, so ist dies kein Problem. Stellen Sie sicher, dass Sie dabei über puts bleiben, was Ihre Ausgabe verstümmelt.

Sobald Sie sich für @sender.send_data in der Mutex sind, sind Sie sicher, vorausgesetzt, @sender wird anderswo im Code von einem anderen Thread nicht abgerufen. Natürlich, je mehr synchronize Sie werfen, desto mehr blockieren Ihre Threads aufeinander und verlieren die Leistung, so dass Sie ein Gleichgewicht finden müssen, um Ihr Design zu finden.

Tun Sie, was Sie können, um Ihren Code funktionsfähig zu machen: Versuchen Sie, nur den lokalen Status zu verwenden und Methoden zu schreiben, die keine Nebenwirkungen haben. Wenn Ihre Aufgabe komplizierter wird, gibt es Bibliotheken wie concurrent-ruby mit threadsafe Datenstrukturen und anderen Mustern, die helfen können.

+1

Für was es wert ist, 'Logger' ist Threadssafe, so sollte die Ausgabe wie erwartet sein. – coreyward

+0

Ausgezeichnet, danke @coreyward. Wenn jemand einen schnellen empirischen Test wünscht, 'Array.new (500) {| i | Thread.new {1000.x {| j | Rails.logger.info "# {i} - # {j}"}}}. Jedes (&: join) '. –

Verwandte Themen