806 Stimmen

Wie sollte ich Multithreading-Code in der Einheit testen?

Bisher habe ich den Albtraum des Testens von Multithreading-Code vermieden, da er mir einfach zu sehr wie ein Minenfeld erscheint. Ich möchte fragen, wie die Leute über das Testen von Code, der auf Threads für die erfolgreiche Ausführung angewiesen ist, oder einfach nur, wie die Leute über das Testen dieser Art von Fragen, die nur zeigen, wenn zwei Threads in einer bestimmten Weise interagieren gegangen sind?

Dies scheint ein wirklich zentrales Problem für die Programmierer heute, wäre es nützlich, unser Wissen auf dieser einen imho Pool.

2 Stimmen

Ich hatte vor, eine Frage zu genau diesem Thema zu stellen. Obwohl Will im Folgenden viele gute Punkte anführt, denke ich, dass wir es besser machen können. Ich stimme zu, dass es keinen einzigen "Ansatz" gibt, um das Problem sauber zu lösen. Aber "so gut wie möglich zu testen" setzt die Messlatte sehr niedrig an. Ich werde mit meinen Ergebnissen zurückkommen.

0 Stimmen

In Java: Das Paket java.util.concurrent enthält einige schlecht bekannte Klassen, die helfen können, deterministische JUnit-Tests zu schreiben. Werfen Sie einen Blick auf - CountDownLatch - Semaphor - Austauscher

0 Stimmen

Können Sie bitte einen Link zu Ihrer vorherigen Frage zu Unit-Tests angeben?

4voto

feldoh Punkte 648

Ich habe vor kurzem (für Java) ein Tool namens Threadsafe entdeckt. Es ist ein statisches Analysewerkzeug, ähnlich wie findbugs, aber speziell zum Aufspüren von Multithreading-Problemen. Es ist kein Ersatz für das Testen, aber ich kann es als Teil des Schreibens von zuverlässigem Multithreading-Java empfehlen.

Es fängt sogar einige sehr subtile potenzielle Probleme ab, wie z. B. die Subsumierung von Klassen, den Zugriff auf unsichere Objekte durch konkurrierende Klassen und das Aufspüren fehlender flüchtiger Modifikatoren bei der Verwendung des Paradigmas der doppelten Sperrprüfung.

Wenn Sie multithreaded Java schreiben Probieren Sie es aus.

3voto

Effi Bar-She'an Punkte 722

Der folgende Artikel schlägt 2 Lösungen vor. Wrapping einer Semaphore (CountDownLatch) und Hinzufügen von Funktionen wie Externalisieren von Daten aus dem internen Thread. Eine andere Möglichkeit, diesen Zweck zu erreichen, ist die Verwendung von Thread Pool (siehe Points of Interest).

Sprinkler - Erweitertes Synchronisationsobjekt

3voto

Dale Ragan Punkte 18091

Ich hatte die unglückliche Aufgabe, Threaded Code zu testen, und es sind definitiv die schwierigsten Tests, die ich je geschrieben habe.

Beim Schreiben meiner Tests habe ich eine Kombination aus Delegierten und Ereignissen verwendet. Im Grunde geht es um die Verwendung von PropertyNotifyChanged Ereignisse mit einer WaitCallback oder eine Art von ConditionalWaiter die Umfragen.

Ich bin mir nicht sicher, ob dies der beste Ansatz war, aber er hat sich für mich bewährt.

2voto

Paul Nathan Punkte 38618

Ich habe die meiste Zeit der letzten Woche in einer Universitätsbibliothek verbracht und mich mit dem Debugging von nebenläufigem Code beschäftigt. Das zentrale Problem ist, dass nebenläufiger Code nicht-deterministisch ist. Typischerweise ist das akademische Debugging hier in eines der drei Lager gefallen:

  1. Ereignisverfolgung/Wiederholung. Dies erfordert eine Ereignisüberwachung und die Überprüfung der gesendeten Ereignisse. In einem UT-Framework würde dies das manuelle Senden der Ereignisse als Teil eines Tests und die anschließende Post-Mortem-Überprüfung beinhalten.
  2. Skriptfähig. Hier interagieren Sie mit dem laufenden Code durch eine Reihe von Auslösern. "On x > foo, baz()". Dies könnte in ein UT-Framework interpretiert werden, in dem ein Laufzeitsystem einen bestimmten Test bei einer bestimmten Bedingung auslöst.
  3. Interaktiv. Das funktioniert natürlich nicht in einer automatischen Prüfungssituation ;)

Wie die oben genannten Kommentatoren bemerkt haben, können Sie Ihr nebenläufiges System nun in einen deterministischeren Zustand versetzen. Wenn man das jedoch nicht richtig macht, ist man wieder bei der Entwicklung eines sequenziellen Systems angelangt.

Mein Vorschlag wäre, sich darauf zu konzentrieren, ein sehr striktes Entwurfsprotokoll darüber zu haben, was mit einem Gewinde versehen wird und was nicht mit einem Gewinde versehen wird. Wenn Sie Ihre Schnittstelle so einschränken, dass es nur minimale Abhängigkeiten zwischen den Elementen gibt, ist es viel einfacher.

Viel Glück, und arbeiten Sie weiter an Ihrem Problem.

1voto

KolA Punkte 657

Unter der Annahme, dass mit "Multithreading"-Code etwas gemeint war, das

  • zustandsabhängig und veränderlich
  • AND von mehreren Threads aufgerufen/verändert gleichzeitig

Mit anderen Worten: Es geht um die Prüfung benutzerdefinierte zustandsabhängige thread-sichere Klasse/Methode/Einheit - was heutzutage ein sehr seltenes Tier sein dürfte.

Da dieses Biest selten ist, müssen wir zunächst sicherstellen, dass es alle gültigen Ausreden gibt, um es zu schreiben.

Schritt 1. Erwägen Sie die Änderung des Zustands im gleichen Synchronisationskontext.

Heutzutage ist es einfach, kompositionsfähigen nebenläufigen und asynchronen Code zu schreiben, bei dem IO oder andere langsame Operationen in den Hintergrund verlagert werden, der gemeinsame Zustand aber in einem Synchronisationskontext aktualisiert und abgefragt wird. z.B. async/await Tasks und Rx in .NET etc. - Sie alle sind vom Design her testbar, "echte" Tasks und Scheduler können ersetzt werden, um das Testen deterministisch zu machen (was jedoch nicht in den Rahmen dieser Frage fällt).

Es mag sehr gezwungen klingen, aber dieser Ansatz funktioniert überraschend gut. Es ist möglich, ganze Anwendungen in diesem Stil zu schreiben, ohne dass ein Zustand thread-sicher gemacht werden muss (ich tue das).

Schritt 2. Wenn eine Manipulation des gemeinsamen Zustands in einem einzelnen Synchronisationskontext absolut nicht möglich ist.

Vergewissern Sie sich, dass das Rad nicht neu erfunden wird / dass es definitiv keine Standardalternative gibt, die für die Aufgabe angepasst werden kann. Es sollte wahrscheinlich sein, dass der Code sehr kohärent und in einer Einheit enthalten ist, z.B. mit einer guten Chance, dass es sich um einen Spezialfall einer standardmäßigen thread-sicheren Datenstruktur wie Hash-Map oder Collection oder was auch immer handelt.

Hinweis: Wenn der Code groß ist / sich über mehrere Klassen erstreckt UND Multithread-Zustandsmanipulationen benötigt, dann ist die Wahrscheinlichkeit sehr hoch, dass das Design nicht gut ist, überdenken Sie Schritt 1

Schritt 3. Wenn dieser Schritt erreicht ist, müssen wir prüfen unsere eigene benutzerdefinierte zustandsabhängige thread-sichere Klasse/Methode/Einheit .

Ich will ganz ehrlich sein: Ich musste noch nie richtige Tests für solchen Code schreiben. Meistens komme ich bei Schritt 1 davon, manchmal bei Schritt 2. Das letzte Mal, dass ich benutzerdefinierten Thread-sicheren Code schreiben musste, ist so viele Jahre her, dass es war, bevor ich Unit-Tests angenommen / wahrscheinlich würde ich es mit dem aktuellen Wissen sowieso nicht schreiben müssen.

Wenn ich einen solchen Code wirklich testen müsste ( endlich, die eigentliche Antwort ), dann würde ich die folgenden Dinge versuchen

  1. Nicht-deterministische Belastungstests. z.B. 100 Threads gleichzeitig ausführen und prüfen, ob das Endergebnis konsistent ist. Dies ist eher typisch für Tests auf höherer Ebene/Integrationstests von Szenarien mit mehreren Benutzern, kann aber auch auf der Ebene der Einheiten verwendet werden.

  2. Einige Test-'Hooks' bereitstellen, in die der Test Code einspeisen kann, um deterministische Szenarien zu erstellen, in denen ein Thread eine Operation vor dem anderen ausführen muss. So hässlich es auch ist, ich kann mir nichts Besseres vorstellen.

  3. Verzögerungsgesteuerte Tests, um Threads in einer bestimmten Reihenfolge ablaufen zu lassen und Operationen durchzuführen. Streng genommen sind solche Tests auch nicht-deterministisch (es gibt eine Chance des Systems einfrieren / stop-the-world GC Sammlung, die sonst orchestriert Verzögerungen verzerren kann), auch ist es hässlich, aber erlaubt es, Haken zu vermeiden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X