Frage

Wir haben eine Codebasis, die mehrere Jahre alt ist, und alle ursprünglichen Entwickler sind längst verschwunden.Es verwendet viele, viele Threads, jedoch ohne erkennbares Design oder gemeinsame Architekturprinzipien.Jeder Entwickler hatte seinen eigenen Stil der Multithread-Programmierung, daher kommunizieren einige Threads über Warteschlangen miteinander, andere sperren Daten mit Mutexes, andere sperren mit Semaphoren, einige nutzen IPC-Mechanismen des Betriebssystems für die prozessinterne Kommunikation.Es gibt keine Designdokumentation und Kommentare sind spärlich.Es ist ein Chaos, und es scheint, dass wir jedes Mal, wenn wir versuchen, den Code umzugestalten oder neue Funktionen hinzuzufügen, Deadlocks oder andere Probleme verursachen.

Kennt jemand irgendwelche Tools oder Techniken, die dabei helfen würden, alle Interaktionen zwischen Threads zu analysieren und zu dokumentieren?FWIW, die Codebasis ist C++ unter Linux, aber ich würde gerne etwas über Tools für andere Umgebungen erfahren.


Aktualisieren

Ich schätze die bisher erhaltenen Antworten, aber ich hatte auf etwas ausgefeilteres oder systematischeres gehofft als Ratschläge, die im Wesentlichen "Protokollnachrichten hinzufügen, herausfinden, was los ist, und es beheben". Es gibt viele Tools zum Analysieren und Dokumentieren des Kontrollflusses in Single-Thread-Programmen.Gibt es nichts für Multithread-Programme?


Siehe auch Debuggen von Multithread-Anwendungen

War es hilfreich?

Lösung

Investieren Sie in eine Kopie von Intel VTune und seine Gewindeprofilierungswerkzeuge.Sie erhalten sowohl eine System- als auch eine Quellansicht des Thread-Verhaltens.Es wird die Sache sicherlich nicht automatisch für Sie dokumentieren, aber es sollte eine echte Hilfe sein, zumindest zu visualisieren, was unter verschiedenen Umständen passiert.

Ich denke, es gibt eine Testversion, die Sie herunterladen können, es lohnt sich also, es auszuprobieren.Ich habe nur die Windows-Version verwendet, aber auf der VTune-Webseite gibt es auch eine Linux-Version.

Andere Tipps

Als Ausgangspunkt wäre ich versucht, Tracing-Log-Meldungen an strategischen Punkten innerhalb Ihrer Anwendung hinzuzufügen.Auf diese Weise können Sie analysieren, wie Ihre Threads interagieren, ohne dass die Gefahr besteht, dass die Beobachtung der Threads ihr Verhalten ändert (wie es beim schrittweisen Debuggen der Fall sein könnte).Ich habe Erfahrung mit der .NET-Plattform und mein bevorzugtes Protokollierungstool wäre log4net, da es kostenlos ist, umfangreiche Konfigurationsoptionen bietet und, wenn Sie bei der Implementierung Ihrer Protokollierung vernünftig vorgehen, die Leistung Ihrer Anwendung nicht merklich beeinträchtigt.Alternativ gibt es die in .NET integrierte Debug- (oder Trace-)Klasse im System.Diagnostics-Namespace.

Ich würde mich zunächst auf die Shared-Memory-Sperren konzentrieren (die Mutexe und Semaphoren), da diese am wahrscheinlichsten Probleme verursachen.Sehen Sie sich an, welcher Staat durch Sperren geschützt ist, und ermitteln Sie dann, welcher Staat durch mehrere Sperren geschützt ist.Dadurch erhalten Sie ein Gefühl für mögliche Konflikte.Schauen Sie sich Situationen an, in denen Code, der eine Sperre hält, Methoden aufruft (virtuelle Methoden nicht vergessen).Versuchen Sie, diese Anrufe nach Möglichkeit zu eliminieren (indem Sie die Dauer der Sperre verkürzen).

Weisen Sie anhand der Liste der gehaltenen Mutexe und einer ungefähren Vorstellung von dem Zustand, den sie schützen, eine Sperrreihenfolge zu (d. h. Mutex A sollte immer vor Mutex B verwendet werden).Versuchen Sie, dies im Code durchzusetzen.

Prüfen Sie, ob Sie mehrere Sperren zu einer kombinieren können, wenn die Parallelität dadurch nicht beeinträchtigt wird.Wenn beispielsweise Mutex A und B den Eindruck erwecken, dass sie möglicherweise Deadlocks haben und ein Ordnungsschema nicht einfach zu erstellen ist, kombinieren Sie sie zunächst zu einer Sperre.

Es wird nicht einfach sein, aber ich bin dafür, den Code auf Kosten der Parallelität zu vereinfachen, um das Problem in den Griff zu bekommen.

Dies ist ein wirklich schwieriges Problem für automatisierte Tools.Vielleicht möchten Sie einen Blick darauf werfen Modellprüfung dein Code.Erwarten Sie keine magischen Ergebnisse:Modellprüfer sind hinsichtlich der Menge an Code und der Anzahl der Threads, die sie effektiv überprüfen können, sehr begrenzt.

Ein Tool, das für Sie funktionieren könnte, ist SCHACH (obwohl es leider nur für Windows verfügbar ist). EXPLOSION ist ein weiteres ziemlich leistungsfähiges Tool, das jedoch sehr schwierig zu verwenden ist und möglicherweise nicht mit C++ umgehen kann.Wikipedia listet auch auf Dampf, von dem ich noch nie gehört habe, aber es hört sich so an, als ob es für Sie funktionieren könnte:

StEAM ist ein Modellprüfer für C++.Es erkennt Deadlocks, Segmentierungsfehler, Variablen außerhalb des gültigen Bereichs und nicht terminierende Schleifen.

Alternativ würde es wahrscheinlich sehr hilfreich sein, zu versuchen, den Code auf eine kleine Anzahl wohldefinierter (und vorzugsweise hochrangiger) Synchronisationsschemata zu konvergieren.Das Mischen von Sperren, Semaphoren und Monitoren in derselben Codebasis kann zu Problemen führen.

Beachten Sie bei der Verwendung von log4net oder einem ähnlichen Tool, dass diese das Timing der Anwendung ändern und häufig die zugrunde liegenden Rennbedingungen verbergen können.Wir hatten schlecht geschriebenen Code zum Debuggen und führten die Protokollierung ein, wodurch Race Conditions und Deadlocks tatsächlich beseitigt wurden (oder deren Häufigkeit erheblich reduziert wurde).

In Java haben Sie Auswahlmöglichkeiten wie FindBugs (zur statischen Bytecode-Analyse), um bestimmte Arten inkonsistenter Synchronisierung zu finden, oder die vielen dynamischen Thread-Analysatoren von Unternehmen wie Coverity, JProbe, OptimizeIt usw.

Kann Ihnen UML hier nicht weiterhelfen?

Wenn Sie Ihre Codebasis rückentwickeln UML, dann sollten Sie in der Lage sein, Klassendiagramme zu zeichnen, die die Beziehungen zwischen Ihren Klassen zeigen.Ausgehend von den Klassen, deren Methoden die Thread-Einstiegspunkte sind, können Sie sehen, welcher Thread welche Klasse verwendet.Basierend auf meiner Erfahrung mit Rational Rose, dies könnte per Drag-and-Drop erreicht werden;Wenn keine Beziehung zwischen der hinzugefügten Klasse und den vorherigen besteht, wird die hinzugefügte Klasse nicht direkt von dem Thread verwendet, der mit der Methode gestartet hat, mit der Sie das Diagramm begonnen haben.Dies sollte Ihnen Hinweise auf die Rolle der einzelnen Threads geben.

Dadurch werden auch die „Datenobjekte“ angezeigt, die gemeinsam genutzt werden, und die Objekte, die threadspezifisch sind.

Wenn Sie ein großes Klassendiagramm zeichnen und alle „Datenobjekte“ entfernen, sollten Sie in der Lage sein, dieses Diagramm als Wolken zu gestalten, wobei jede Wolke ein Thread – oder eine Gruppe von Threads – ist, es sei denn, die Kopplung und Kohäsion der Codebasis ist dies schrecklich.

Dadurch erhalten Sie nur einen Teil des Rätsels, könnten aber hilfreich sein.Ich hoffe nur, dass Ihre Codebasis nicht zu matschig oder zu „prozedural“ ist. In diesem Fall ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top