Ich bin in den letzten Jahren mehrmals auf dieses Problem gestoßen, als ich Code für die Thread-Behandlung für mehrere Projekte geschrieben habe. Ich gebe eine späte Antwort, da die meisten anderen Antworten, obwohl sie Alternativen bieten, die Frage zum Testen nicht wirklich beantworten. Meine Antwort richtet sich an Fälle, in denen es keine Alternative zu Multithread-Code gibt. Der Vollständigkeit halber gehe ich auf Fragen des Code-Designs ein, diskutiere aber auch Unit-Tests.
Schreiben von testbarem Multithread-Code
Das erste, was Sie tun müssen, ist, Ihren Produktions-Thread-Bearbeitungscode von dem gesamten Code zu trennen, der die eigentliche Datenverarbeitung ausführt. Auf diese Weise kann die Datenverarbeitung als Code mit einfachem Thread getestet werden, und der Multithread-Code kann nur Threads koordinieren.
Das zweite, woran Sie sich erinnern sollten, ist, dass Fehler in Multithread-Code wahrscheinlich sind. Die Fehler, die sich am seltensten manifestieren, sind die Fehler, die sich in die Produktion einschleichen, selbst in der Produktion schwer zu reproduzieren sind und daher die größten Probleme verursachen. Aus diesem Grund ist der Standard-Codierungsansatz, den Code schnell zu schreiben und dann zu debuggen, bis er funktioniert, eine schlechte Idee für Multithread-Code. Dies führt zu Code, in dem die einfachen Fehler behoben sind und die gefährlichen Fehler immer noch vorhanden sind.
Stattdessen müssen Sie beim Schreiben von Multithread-Code den Code mit der Einstellung schreiben, dass Sie das Schreiben der Fehler überhaupt vermeiden möchten. Wenn Sie den Datenverarbeitungscode ordnungsgemäß entfernt haben, sollte der Thread-Verarbeitungscode so klein sein - vorzugsweise einige Zeilen, im schlimmsten Fall einige Dutzend Zeilen -, dass Sie die Möglichkeit haben, ihn zu schreiben, ohne einen Fehler zu schreiben, und sicherlich ohne viele Fehler zu schreiben Wenn Sie das Einfädeln verstehen, nehmen Sie sich Zeit und seien Sie vorsichtig.
Schreiben von Komponententests für Multithread-Code
Sobald der Multithread-Code so sorgfältig wie möglich geschrieben wurde, lohnt es sich immer noch, Tests für diesen Code zu schreiben. Der Hauptzweck der Tests besteht nicht darin, auf stark zeitabhängige Race-Condition-Fehler zu testen - es ist unmöglich, diese Race-Bedingungen wiederholt zu testen -, sondern vielmehr darauf, zu testen, ob Ihre Sperrstrategie zur Verhinderung solcher Fehler die Interaktion mehrerer Threads wie beabsichtigt ermöglicht .
Um das korrekte Sperrverhalten ordnungsgemäß zu testen, muss ein Test mehrere Threads starten. Um den Test wiederholbar zu machen, möchten wir, dass die Interaktionen zwischen den Threads in einer vorhersehbaren Reihenfolge stattfinden. Wir möchten die Threads im Test nicht extern synchronisieren, da dadurch Fehler maskiert werden, die in der Produktion auftreten können, wenn die Threads nicht extern synchronisiert sind. Dadurch bleiben Zeitverzögerungen für die Thread-Synchronisation übrig. Dies ist die Technik, die ich erfolgreich angewendet habe, wenn ich Tests mit Multithread-Code schreiben musste.
Wenn die Verzögerungen zu kurz sind, wird der Test fragil, da geringfügige Zeitunterschiede - beispielsweise zwischen verschiedenen Maschinen, auf denen die Tests ausgeführt werden können - dazu führen können, dass das Timing deaktiviert ist und der Test fehlschlägt. Was ich normalerweise getan habe, ist, mit Verzögerungen zu beginnen, die zu Testfehlern führen, die Verzögerungen zu erhöhen, damit der Test zuverlässig auf meinem Entwicklungscomputer bestanden wird, und dann die Verzögerungen darüber hinaus zu verdoppeln, damit der Test eine gute Chance hat, andere Computer zu bestehen. Dies bedeutet, dass der Test eine makroskopische Zeit in Anspruch nimmt, obwohl nach meiner Erfahrung ein sorgfältiges Testdesign diese Zeit auf nicht mehr als ein Dutzend Sekunden beschränken kann. Da Ihre Anwendung nicht sehr viele Stellen enthalten sollte, an denen Thread-Koordinierungscode erforderlich ist, sollte dies für Ihre Testsuite akzeptabel sein.
Verfolgen Sie abschließend die Anzahl der Fehler, die von Ihrem Test erfasst wurden. Wenn Ihr Test eine Codeabdeckung von 80% aufweist, kann davon ausgegangen werden, dass etwa 80% Ihrer Fehler erkannt werden. Wenn Ihr Test gut konzipiert ist, aber keine Fehler findet, besteht eine vernünftige Wahrscheinlichkeit, dass Sie keine zusätzlichen Fehler haben, die nur in der Produktion auftreten. Wenn der Test ein oder zwei Fehler entdeckt, haben Sie möglicherweise immer noch Glück. Darüber hinaus möchten Sie möglicherweise eine sorgfältige Überprüfung oder sogar eine vollständige Neufassung Ihres Thread-Handhabungscodes in Betracht ziehen, da der Code wahrscheinlich immer noch versteckte Fehler enthält, die nur sehr schwer zu finden sind, bis der Code in Produktion ist, und zwar sehr dann schwer zu reparieren.