Testen der Bedingungen für Rennen mit mehreren Threads

54

Lesen Sie die Kommentare zu dieser Antwort , insbesondere:

Nur weil Sie keinen Test schreiben können, heißt das noch lange nicht, dass er nicht kaputt ist. Undefiniertes Verhalten, das normalerweise wie erwartet funktioniert (C und C ++ sind voll davon), Rennbedingungen, mögliche Neuordnung aufgrund eines schwachen Speichermodells ... - CodesInChaos vor 7 Stunden

@CodesInChaos Wenn der Code nicht reproduziert werden kann, kann er auch nicht getestet werden. Und ungetesteten Code ins Leben zu rufen ist meiner Meinung nach ein schlimmeres Verbrechen - RhysW vor 5 Stunden

... hat mich gefragt, ob es generell gute Möglichkeiten gibt, konsequent sehr selten auftretende Produktionsprobleme auszulösen, die durch Rennbedingungen im Testfall verursacht werden.

Dan Neely
quelle
1
schritt durch (montage) anweisung für anweisung an beiden enden
ratschenfreak
1
Eine statische Analyse kann oft ein potenzielles UB aufzeigen, und es ist nicht klar, ob dies als Test gezählt wird
jk.
Tut mir leid zu fragen, aber was bedeutet "UB"?
Doug
2
Schöne Frage, ich wäre interessant, die möglichen Lösungen dafür zu sehen.
RhysW
1
@Doug Undefiniertes Verhalten, einschließlich, aber nicht beschränkt auf Rennbedingungen
jk.

Antworten:

85

Nachdem ich seit etwa 1978 in diesem verrückten Geschäft tätig war und fast die ganze Zeit im Bereich Embedded Real-Time Computing verbracht hatte, arbeitete ich mit Multitasking, Multithreading und Multi-Was-Systemen, manchmal mit mehreren physischen Prozessoren, und habe mehr als meinen fairen Anteil an der Konkurrenz gejagt Meine überlegte Meinung ist, dass die Antwort auf Ihre Frage ganz einfach ist.

Nein.

Es gibt keine gute allgemeine Möglichkeit, beim Testen eine Rennbedingung auszulösen.

Ihre einzige Hoffnung ist es, sie vollständig aus Ihrem System heraus zu entwerfen.

Wann und wenn Sie feststellen, dass jemand anderes einen hineingestopft hat, sollten Sie ihn auf einen Ameisenhaufen setzen und ihn dann neu designen, um ihn zu beseitigen. Nachdem Sie seinen Fauxpas (ausgesprochen f *** up) aus Ihrem System heraus entworfen haben, können Sie ihn von den Ameisen befreien. (Wenn die Ameisen ihn bereits verzehrt haben und nur Knochen zurücklassen, setzen Sie ein Schild mit der Aufschrift "Das passiert mit Leuten, die die Rennbedingungen in das XYZ-Projekt einbauen!" Und LASSEN SIE IHN DORT.)

John R. Strohm
quelle
22
Ich stimme vollkommen zu. Mit anderen Worten, das ist dem Witz sehr ähnlich - Patient: "Doktor, es tut weh, wenn ich das tue ..." Doktor: "Dann hör auf damit!"
Mark Rushakoff
Gute Antwort. Wenn etwas ein nicht testbares Problem verursacht, versuchen Sie zunächst, es zu umgehen. Vermeiden Sie das Problem vollständig!
RhysW
Meine einzige Frage ist: Wie groß sollte ein Ameisenhaufen sein? (+1 BTW).
Peter K.
15
+1 für die korrekte Aussprache von faux pas . (Und der Rest der Antwort.)
Blrfl
1
@PeterK. Dies ist einer der wenigen Fälle , in Software - Entwicklung, zusammen mit Monitoren, RAM und Festplattenlaufwerke, in denen größere IS besser.
John R. Strohm
16

Wenn Sie sich in der MS-Toolkette befinden. Frau Research hat ein Tool erstellt, das für jeden Lauf neue Zwischenzeiten erzwingt und fehlgeschlagene Läufe, das so genannte Schach, neu erstellen kann .

Hier ist ein Video, das es im Einsatz zeigt.

Wiederholung
quelle
5
Das sieht beeindruckend aus; Ich muss irgendwann Zeit finden, es auszuprobieren.
Dan Neely
16

Das beste Tool, das ich für diese Art von Problemen kenne, ist eine Erweiterung von Valgrind namens Helgrind .

Grundsätzlich simuliert Valgrind einen virtuellen Prozessor und führt Ihre Binärdatei (unverändert) darüber aus, sodass jeder einzelne Speicherzugriff überprüft werden kann. Unter Verwendung dieses Frameworks ruft das Helgrind-Überwachungssystem den Schluss auf, wenn der Zugriff auf eine gemeinsam genutzte Variable nicht ordnungsgemäß durch einen Mechanismus zum gegenseitigen Ausschluss geschützt ist. Auf diese Weise kann eine theoretische Rassenbedingung erkannt werden, auch wenn sie nicht tatsächlich eingetreten ist.

Intel verkauft ein sehr ähnliches Tool namens Intel Inspector .

Diese Tools liefern großartige Ergebnisse, Ihr Programm wird jedoch während der Analyse erheblich langsamer.

Julien
quelle
1
ist Valgrind noch ein * nix only Tool?
Dan Neely
1
Ja, Linux, MacOSX, Android und etwas BSD: valgrind.org/info/platforms.html
Julien
1
ThreadSanitizer ist ein ähnliches Tool. Es funktioniert anders als Helgrind, was den Vorteil mit sich bringt, dass es viel schneller ist, aber eine Integration in die Toolchain erfordert.
Sebastian Redl
7

Um einen Multithreading-Fehler aufzudecken, müssen verschiedene Ausführungsthreads gezwungen werden, ihre Schritte in einer bestimmten verschachtelten Reihenfolge auszuführen. Normalerweise ist dies schwierig, ohne manuelles Debuggen oder Manipulieren des Codes, um eine Art "Handle" zur Steuerung dieser Verschachtelung zu erhalten. Das Ändern von Code, der sich unvorhersehbar verhält, wirkt sich jedoch häufig auf diese Unvorhersehbarkeit aus. Daher ist dies schwer zu automatisieren.

Ein netter Trick wird von Jaroslav Tulach in Practical API Design beschrieben : Wenn Sie im fraglichen Code Protokollierungsanweisungen haben, manipulieren Sie den Verbraucher dieser Protokollierungsanweisungen (z. B. ein injiziertes Pseudoterminal), damit er die einzelnen Protokollnachrichten in einem bestimmten akzeptiert Auftrag basiert auf ihrem Inhalt. Auf diese Weise können Sie die Verschachtelung von Schritten in verschiedenen Threads steuern, ohne dem Produktionscode etwas hinzufügen zu müssen, das noch nicht vorhanden ist.

Kilian Foth
quelle
2
Ich habe vor der Verwendung von injizierten Repositorys ähnliche Maßnahmen ergriffen, um die Threads zu deaktivieren, die sie in bestimmten Reihenfolgen aufrufen, um das gewünschte Interleave zu erzwingen. Nachdem ich Code geschrieben habe, der das tut, bin ich geneigt, die oben stehende Antwort von +1 @ John zu beantworten. Ernsthaft, es ist so schmerzhaft, dieses Zeug richtig einzusetzen, und es gibt immer noch nur die besten Vermutungsgarantien, weil es leicht unterschiedliche Zwischenblätter mit unterschiedlichen Ergebnissen geben kann. der bessere Ansatz ist, einfach alle möglichen Rennbedingungen durch statische Analyse und / oder sorgfältiges Kämmen von Code für jeden gemeinsamen Zustand zu eliminieren
Jimmy Hoffa,
6

Es gibt keine Möglichkeit, absolut sicher zu sein, dass verschiedene Arten von undefiniertem Verhalten (insbesondere Rennbedingungen) nicht existieren.

Es gibt jedoch eine Reihe von Tools, die eine gute Anzahl solcher Situationen aufzeigen. Möglicherweise können Sie nachweisen, dass derzeit ein Problem mit solchen Tools vorliegt, obwohl Sie nicht nachweisen können, dass Ihr Fix gültig ist.

Einige interessante Tools für diesen Zweck:

Valgrind ist ein Gedächtnisprüfer. Es findet Speicherlecks, liest nicht initialisierten Speicher, verwendet baumelnde Zeiger und grenzüberschreitende Zugriffe.

Helgrind ist ein Fadensicherheitsprüfer. Es findet Rennbedingungen.

Beide arbeiten mit dynamischer Instrumentierung, dh sie nehmen Ihr Programm wie es ist und führen es in einer virtualisierten Umgebung aus. Das macht sie unaufdringlich, aber langsam.

UBSan ist ein undefinierter Verhaltensüberprüfer. Es werden verschiedene Fälle von undefiniertem Verhalten in C und C ++ gefunden, z. B. Integer-Überläufe, Verschiebungen außerhalb des Bereichs und ähnliches.

MSan ist ein Speicher-Checker. Es hat ähnliche Ziele wie Valgrind.

TSan ist ein Thread-Sicherheitsprüfer. Es hat ähnliche Ziele wie Helgrind.

Diese drei Funktionen sind in den Clang-Compiler integriert und generieren beim Kompilieren Code. Dies bedeutet, dass Sie sie in Ihren Erstellungsprozess integrieren müssen (insbesondere müssen Sie sie mit Clang kompilieren). Dies macht sie anfangs viel schwieriger einzurichten als * grind, hat aber andererseits einen viel geringeren Laufzeitaufwand.

Alle aufgelisteten Tools funktionieren unter Linux und einige unter MacOS. Ich denke noch keine Arbeit an Windows zuverlässig.

Sebastian Redl
quelle
1

Es scheint, dass die meisten Antworten hier diese Frage als "Wie erkenne ich Rennbedingungen automatisch?" wenn die Frage wirklich ist "Wie reproduziere ich Rennbedingungen beim Testen, wenn ich sie finde?"

Die Möglichkeit besteht darin, in Ihrem Code eine Synchronisierung einzuführen, die nur zu Testzwecken verwendet wird. Wenn beispielsweise eine Racebedingung eintritt, wenn Ereignis X zwischen Ereignis A und Ereignis B eintritt, schreiben Sie zum Testen Ihrer Anwendung einen Code, der darauf wartet, dass Ereignis X nach Ereignis A eintritt. Sie werden wahrscheinlich eine Möglichkeit benötigen, damit Ihre Tests mit Ihrer Anwendung kommunizieren können, um dies mitzuteilen ("Hey, ich teste dieses Ding, also warten Sie an dieser Stelle auf dieses Ereignis").

Ich verwende node.js und mongo, wobei einige Aktionen das Erstellen konsistenter Daten in mehreren Sammlungen beinhalten. In diesen Fällen rufen meine Komponententests die Anwendung auf und teilen ihr mit, dass sie "ein Warten auf Ereignis X" eingerichtet haben. Sobald die Anwendung es eingerichtet hat, wird der Test für Ereignis X ausgeführt und die Tests geben anschließend Auskunft Die Anwendung ("Ich bin fertig mit dem Warten auf Ereignis X"), damit der Rest der Tests normal ausgeführt wird.

Die Antwort hier erklärt diese Art von Dingen im Zusammenhang mit Python im Detail: https://stackoverflow.com/questions/19602535/how-can-i-reproduce-the-race-conditions-in-this-python-code- zuverlässig

BT
quelle