Akteursberechnungsmodell mit boost :: thread

8

Ich versuche Actor-Berechnungsmodell über Threads in C ++ zu implementieren, indem ich boost :: thread verwende. Aber das Programm löst während der Ausführung eine seltsame Ausnahme aus. Ausnahme ist nicht stabil und manchmal funktioniert das Programm korrekt.

Dort mein Code:

actor.hpp

%Vor%

actor.cpp

%Vor%

Die Ausnahme, die geworfen wird, ist boost :: thread_interrupted in int rc = future.get(); line. Aber form boost docs kann ich nicht von dieser Ausnahme halten. Docs sagt

  

Wird ausgelöst: - boost :: thread_interrupted, wenn das mit * verknüpfte Ergebnis zum Zeitpunkt des Aufrufs noch nicht fertig ist und der aktuelle Thread unterbrochen ist.

Aber mein Worker-Thread kann nicht unterbrochen sein.

Als ich gdb benutzt habe und "catch throw" gesetzt habe, sehe ich, dass die Rückverfolgung wie

aussieht
  

throw thread_interrupted

     

boost :: detail :: interruption_checker :: check_for_interruption

     

boost :: detail :: interrupt_checker :: interrupt_checker

     

boost :: condition_variable :: wait

     

boost :: detail :: future_object_base :: wait_internal

     

boost :: detail :: future_object_base :: wait

     

boost :: detail :: future_object :: get

     

boost :: unique_future :: get

Ich habe Boost-Quellen untersucht, kann aber nicht herausfinden, warum interruption_checker entschieden hat, dass der Worker-Thread unterbrochen wird.

Also jemand C ++ Guru, bitte hilf mir. Was muss ich tun, um den richtigen Code zu erhalten? Ich benutze:

boost 1_53

Linux Version 2.6.18-194.32.1.el5 Red Hat 4.1.2-48

gcc 4.7

  

BEARBEITEN

     

Es wurde behoben! Danke an Evgeny Panasyuk und Lazin. Das Problem war in TLS   Management. boost :: thread und boost :: thread_specific_ptr verwenden   gleicher TLS-Speicher für ihre Zwecke. In meinem Fall gab es ein Problem wann   Beide haben versucht, diesen Speicher bei der Erstellung zu ändern (Leider habe ich   hat nicht verstanden, warum im Detail es passiert). So wurde TLS beschädigt.

     

Ich habe boost :: thread_specific_ptr aus meinem Code mit __thread ersetzt   angegebene Variable.

     

Offtop: Während des Debugging habe ich Speicherfehler in der externen Bibliothek gefunden   und repariere es =)

.

  

EDIT 2   Ich habe das genaue Problem ... Es ist ein Fehler in GCC =)   Das _GLIBCXX_DEBUG-Kompilierungsflag unterbricht ABI.   Sie können die Diskussion über Boost Bugtracker sehen:      Ссылка

    
inkooboo 25.10.2013, 15:06
quelle

2 Antworten

5

Ich habe mehrere Fehler gefunden:

Actor::workerThread function wird doppelt auf d_jobQueueMutex entsperrt. Das erste Entsperren ist manuell d_jobQueueMutex.unlock(); , das zweite ist im Destruktor von boost::unique_lock<boost::mutex> .

Sie sollten eine Entsperrung verhindern, zum Beispiel release Zuordnung zwischen unique_lock und mutex :

%Vor%

Oder fügen Sie einen zusätzlichen Codeblock + default-constructed Job hinzu.

Es ist möglich, dass workerThread niemals die folgende Schleife verlässt:

%Vor%

Stellen Sie sich folgenden Fall vor: d_jobQueue ist leer, Actor::~Actor() wird aufgerufen, setzt Flag und benachrichtigt den Arbeitsthread:

%Vor%

workerThread wacht in der while-Schleife auf, sieht, dass die Warteschlange leer ist und wieder schläft.

Es ist üblich, einen speziellen letzten Job zu senden, um den Worker-Thread zu stoppen:

%Vor%

In diesem Fall muss d_keepWorkerRunning nicht atomar sein.

LIVE DEMO auf Coliru

BEARBEITEN :

  

Ich habe den Code für die Ereigniswarteschlange in Ihr Beispiel eingefügt.

Sie haben eine gleichzeitige Warteschlange in EventQueueImpl und Actor , aber für verschiedene Typen. Es ist möglich, einen gemeinsamen Teil in die separate Entity concurrent_queue<T> zu extrahieren, die für jeden Typ funktioniert. Es wäre viel einfacher, die Warteschlange an einer Stelle zu debuggen und zu testen, als Fehler in verschiedenen Klassen zu erfassen.

Sie können also versuchen, concurrent_queue<T> (auf Coliru)

    
Evgeny Panasyuk 28.10.2013, 07:31
quelle
2

Das ist nur eine Vermutung. Ich denke, dass ein Code tatsächlich boost heißen kann: : tread :: interrupt () . Sie können einen Haltepunkt für diese Funktion festlegen und sehen, welcher Code dafür verantwortlich ist. Sie können in execJobSync auf Unterbrechung testen:

%Vor%

Der verdächtigste Code in diesem Fall ist ein Code, der auf ein Thread-Objekt verweist.

Es ist eine gute Übung, die boost :: Thread-Code-Unterbrechung trotzdem bewusst zu machen. Es ist auch möglich, Unterbrechung zu deaktivieren für einige Bereiche.

Wenn dies nicht der Fall ist, müssen Sie den Code überprüfen, der mit lokalem Threadspeicher arbeitet, da das Flag für die Threadunterbrechung im TLS gespeichert ist. Vielleicht schreibt Ihr Code es um. Sie können die Unterbrechung vor und nach einem solchen Codefragment überprüfen.

Eine andere Möglichkeit ist, dass dein Speicher korrupt ist. Wenn kein Code boost :: thread :: interrupt () aufruft und Sie nicht mit TLS arbeiten. Dies ist der schwierigste Fall, versuchen Sie, einen dynamischen Analysator zu verwenden - Valgrind oder Clam Memory Sanitizer.

Offtopic: Wahrscheinlich müssen Sie einige gleichzeitige Warteschlangen verwenden. std :: queue wird wegen hoher Speicherkonkurrenz sehr langsam sein und Sie werden mit schlechter Cache-Leistung enden. Eine gute gleichzeitige Warteschlange ermöglicht es Ihrem Code, Elemente parallel in die Warteschlange einzuordnen oder aus der Warteschlange zu entfernen.

Darsteller ist auch nicht etwas, das willkürlichen Code ausführen soll. Die Actor-Warteschlange muss einfache Nachrichten empfangen, keine Funktionen! Du schreibst eine Jobwarteschlange :) Du musst dir ein Schauspieler-System wie Akka oder libcpa .

    
Lazin 29.10.2013 11:58
quelle

Tags und Links