Ich hatte eine Diskussion mit einem Teamkollegen über das Sperren in .NET. Er ist ein wirklich kluger Kerl mit einem umfassenden Hintergrund in der Programmierung auf niedrigerer und höherer Ebene, aber seine Erfahrung mit der Programmierung auf niedrigerer Ebene übertrifft meine bei weitem. Wie auch immer, er argumentierte, dass .NET-Sperren auf kritischen Systemen vermieden werden sollten, von denen erwartet wird, dass sie unter hoher Last stehen, wenn dies überhaupt möglich ist, um die zugegebenermaßen geringe Wahrscheinlichkeit zu vermeiden, dass ein "Zombie-Thread" ein System zum Absturz bringt. Ich benutze routinemäßig das Sperren und wusste nicht, was ein "Zombiethread" ist, also fragte ich. Der Eindruck, den ich von seiner Erklärung bekam, ist, dass ein Zombiethread ein Thread ist, der beendet wurde, aber irgendwie immer noch an einigen Ressourcen festhält. Ein Beispiel, das er gab, wie ein Zombie-Thread ein System beschädigen könnte, war, dass ein Thread eine Prozedur beginnt, nachdem er ein Objekt gesperrt hat. und wird dann irgendwann beendet, bevor die Sperre aufgehoben werden kann. Diese Situation kann zum Absturz des Systems führen, da bei Versuchen, diese Methode auszuführen, alle Threads auf den Zugriff auf ein Objekt warten, das niemals zurückgegeben wird, da der Thread, der das gesperrte Objekt verwendet, tot ist.
Ich glaube, ich habe das Wesentliche verstanden, aber wenn ich nicht auf der Basis bin, lassen Sie es mich bitte wissen. Das Konzept ergab für mich einen Sinn. Ich war nicht ganz davon überzeugt, dass dies ein echtes Szenario ist, das in .NET passieren kann. Ich habe noch nie von "Zombies" gehört, aber ich erkenne, dass Programmierer, die auf niedrigeren Ebenen eingehend gearbeitet haben, tendenziell ein tieferes Verständnis der Computergrundlagen (wie Threading) haben. Ich sehe jedoch definitiv den Wert des Sperren, und ich habe viele Weltklasse-Programmierer gesehen, die das Sperren wirksam einsetzen. Ich habe auch nur begrenzte Möglichkeiten, dies für mich selbst zu bewerten, weil ich weiß, dass die lock(obj)
Aussage wirklich nur syntaktischer Zucker ist für:
bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }
und weil Monitor.Enter
und Monitor.Exit
markiert sind extern
. Es scheint denkbar, dass .NET eine Art von Verarbeitung durchführt, die Threads vor der Exposition gegenüber Systemkomponenten schützt, die diese Auswirkungen haben könnten. Dies ist jedoch rein spekulativ und basiert wahrscheinlich nur auf der Tatsache, dass ich noch nie von "Zombie-Threads" gehört habe. Vor. Ich hoffe also, dass ich hier ein Feedback dazu bekommen kann:
- Gibt es eine klarere Definition eines "Zombiethreads" als das, was ich hier erklärt habe?
- Können Zombiethreads in .NET auftreten? (Warum Warum nicht?)
- Wie kann ich gegebenenfalls die Erstellung eines Zombiethreads in .NET erzwingen?
- Wie kann ich das Sperren nutzen, ohne ein Zombie-Thread-Szenario in .NET zu riskieren?
Aktualisieren
Ich habe diese Frage vor etwas mehr als zwei Jahren gestellt. Heute ist das passiert:
quelle
wait
oderwaitpid
. Der untergeordnete Prozess wird dann als "Zombie-Prozess" bezeichnet. Siehe auch howtogeek.com/119815Antworten:
Scheint mir eine ziemlich gute Erklärung zu sein - ein Thread, der beendet wurde (und daher keine Ressourcen mehr freigeben kann), dessen Ressourcen (z. B. Handles) noch vorhanden sind und (möglicherweise) Probleme verursachen.
Sie tun es sicher, schau, ich habe eins gemacht!
Dieses Programm startet einen Thread,
Target
der eine Datei öffnet und sich dann sofort mit beendetExitThread
.Der resultierende Zombiethread gibt das Handle für die Datei "test.txt" niemals frei und daher bleibt die Datei geöffnet, bis das Programm beendet wird (Sie können dies mit dem Prozess-Explorer oder ähnlichem überprüfen).Das Handle zu "test.txt" wird erst freigegeben, wennGC.Collect
es aufgerufen wird - es stellt sich heraus, dass es noch schwieriger ist, als ich dachte, einen Zombiethread zu erstellen, der Handles verliert.Tu nicht was ich gerade getan habe!
Solange Ihr Code korrekt nach sich selbst bereinigt wird (verwenden Sie sichere Handles oder gleichwertige Klassen, wenn Sie mit nicht verwalteten Ressourcen arbeiten) und solange Sie sich nicht die Mühe machen, Threads auf seltsame und wunderbare Weise zu beenden (der sicherste Weg ist gerecht Um Threads niemals zu töten - lassen Sie sie sich normal beenden oder bei Bedarf durch Ausnahmen). Der einzige Weg, auf dem Sie etwas haben, das einem Zombiethread ähnelt, ist, wenn etwas sehr schief gelaufen ist (z. B. wenn in der CLR etwas schief geht).
Tatsächlich ist es überraschend schwierig, einen Zombiethread zu erstellen (ich musste P / Invoke in eine Funktion einbinden, die Ihnen in der Dokumentation im Wesentlichen sagt, dass Sie ihn nicht außerhalb von C aufrufen sollen). Zum Beispiel erzeugt der folgende (schreckliche) Code tatsächlich keinen Zombiethread.
Trotz einiger ziemlich schrecklicher Fehler wird das Handle zu "test.txt" immer noch geschlossen, sobald
Abort
es aufgerufen wird (als Teil des Finalizers, fürfile
den unter dem Deckblatt SafeFileHandle verwendet wird , um das Dateihandle zu verpacken).Das Sperrbeispiel in der Antwort von C.Evenhuis ist wahrscheinlich der einfachste Weg, eine Ressource (in diesem Fall eine Sperre) nicht freizugeben, wenn ein Thread auf nicht seltsame Weise beendet wird. Dies lässt sich jedoch leicht beheben, indem
lock
stattdessen entweder eine Anweisung oder eine Anweisung verwendet wird die Freigabe in einenfinally
Block setzen.Siehe auch
lock
Schlüsselwort verwendet wird (jedoch nur in .Net 3.5 und früheren Versionen).quelle
ExitThread
Anruf. Natürlich funktioniert es, aber es fühlt sich eher nach einem cleveren Trick als nach einem realistischen Szenario an. Eines meiner Ziele ist es zu lernen, was ich nicht tun soll, damit ich nicht versehentlich Zombiethreads mit .NET-Code erstelle. Ich hätte wahrscheinlich herausfinden können, dass das Aufrufen von C ++ - Code, von dem bekannt ist, dass er dieses Problem verursacht, aus .NET-Code den gewünschten Effekt erzielen würde. Sie wissen offensichtlich viel über dieses Zeug. Kennen Sie andere Fälle (möglicherweise seltsam, aber nicht seltsam genug, um niemals unbeabsichtigt zu passieren) mit demselben Ergebnis?Set excelInstance = GetObject(, "Excel.Application")
Ich habe meine Antwort ein wenig aufgeräumt, aber das Original unten als Referenz belassen
Es ist das erste Mal, dass ich von dem Begriff Zombies höre, also gehe ich davon aus, dass seine Definition lautet:
Ein Thread, der beendet wurde, ohne alle Ressourcen freizugeben
Wenn Sie diese Definition haben, können Sie dies in .NET wie in anderen Sprachen (C / C ++, Java) tun.
Doch , ich glaube dies nicht als Grund nicht kritisch Code threaded, Mission in .NET zu schreiben. Es kann andere Gründe geben, sich gegen .NET zu entscheiden, aber das Abschreiben von .NET, nur weil Sie Zombiethreads haben können, macht für mich keinen Sinn. Zombie-Threads sind in C / C ++ möglich (ich würde sogar argumentieren, dass es viel einfacher ist, in C durcheinander zu kommen) und viele kritische Apps mit Threads sind in C / C ++ (Handel mit hohem Volumen, Datenbanken usw.).
Fazit Wenn Sie sich gerade für eine Sprache entscheiden, sollten Sie das Gesamtbild berücksichtigen: Leistung, Teamfähigkeit, Zeitplan, Integration in vorhandene Apps usw. Sicher, Zombiethreads sollten Sie sich überlegen , aber da es im Vergleich zu anderen Sprachen wie C so schwierig ist, diesen Fehler in .NET tatsächlich zu machen, denke ich, dass dieses Problem von anderen Dingen wie den oben genannten überschattet wird. Viel Glück!
Ursprüngliche Antwort Zombies † können existieren, wenn Sie keinen richtigen Threading-Code schreiben. Gleiches gilt für andere Sprachen wie C / C ++ und Java. Dies ist jedoch kein Grund, keinen Thread-Code in .NET zu schreiben.
Und wie bei jeder anderen Sprache sollten Sie den Preis kennen, bevor Sie etwas verwenden. Es ist auch hilfreich zu wissen, was unter der Haube passiert, damit Sie mögliche Probleme vorhersehen können.
Zuverlässiger Code für geschäftskritische Systeme ist nicht einfach zu schreiben, egal in welcher Sprache Sie sich befinden. Aber ich bin mir sicher, dass es in .NET nicht unmöglich ist, ihn korrekt auszuführen. Auch AFAIK, .NET-Threading unterscheidet sich nicht wesentlich vom Threading in C / C ++, es verwendet (oder wird daraus erstellt) dieselben Systemaufrufe mit Ausnahme einiger .net-spezifischer Konstrukte (wie die Lightweight-Versionen von RWL und Ereignisklassen).
† Als ich zum ersten Mal von dem Begriff Zombies gehört habe, meinte Ihr Kollege aufgrund Ihrer Beschreibung wahrscheinlich einen Thread, der beendet wurde, ohne alle Ressourcen freizugeben. Dies kann möglicherweise einen Deadlock, einen Speicherverlust oder eine andere schlimme Nebenwirkung verursachen. Dies ist offensichtlich nicht wünschenswert, aber das Herausgreifen von .NET aufgrund dieser Möglichkeit ist wahrscheinlich keine gute Idee, da dies auch in anderen Sprachen möglich ist. Ich würde sogar argumentieren, dass es in C / C ++ einfacher ist, Fehler zu machen als in .NET (besonders in C, wo Sie kein RAII haben), aber viele kritische Apps sind in C / C ++ geschrieben, oder? Es hängt also wirklich von Ihren individuellen Umständen ab. Wenn Sie jede Unze Geschwindigkeit aus Ihrer Anwendung extrahieren und so nah wie möglich an Bare Metal kommen möchten, ist .NET möglicherweise die richtige Wahlnicht die beste Lösung sein. Wenn Sie ein knappes Budget haben und viele Schnittstellen zu Webdiensten / vorhandenen .net-Bibliotheken / usw. herstellen, ist .NET möglicherweise eine gute Wahl.
quelle
extern
stoße. Wenn Sie einen Vorschlag haben, würde ich ihn gerne hören. (2) Ich würde zustimmen, dass dies in .NET möglich ist. Ich würde gerne glauben, dass es mit dem Sperren möglich ist, aber ich habe noch keine zufriedenstellende Antwort gefunden, um dies heute zu rechtfertigenlocking
daslock
Schlüsselwort meinen , dann wahrscheinlich nicht, da es die Ausführung serialisiert. Um den Durchsatz zu maximieren, müssten Sie abhängig von den Eigenschaften Ihres Codes die richtigen Konstrukte verwenden. Ich würde sagen, wenn Sie zuverlässigen Code in c / c ++ / java / was auch immer mit pthreads / boost / thread pools / what schreiben können, dann können Sie ihn auch in C # schreiben. Aber wenn Sie mit keiner Bibliothek zuverlässigen Code in einer Sprache schreiben können, bezweifle ich, dass das Schreiben in C # anders sein wird.Im Moment wurde der größte Teil meiner Antwort durch die folgenden Kommentare korrigiert. Ich werde die Antwort nicht löschen,
da ich die Reputationspunkte benötige,da die Informationen in den Kommentaren für die Leser wertvoll sein können.Immortal Blue wies darauf hin, dass in .NET 2.0 und höher
finally
Blöcke immun gegen Thread-Abbrüche sind. Und wie von Andreas Niedermair kommentiert, ist dies möglicherweise kein tatsächlicher Zombiethread, aber das folgende Beispiel zeigt, wie das Abbrechen eines Threads Probleme verursachen kann:Bei Verwendung eines
lock() { }
Blocks wird der Blockfinally
jedoch weiterhin ausgeführt, wenn a aufThreadAbortException
diese Weise ausgelöst wird.Wie sich herausstellt, gelten die folgenden Informationen nur für .NET 1 und .NET 1.1:
Wenn innerhalb des
lock() { }
Blocks eine andere Ausnahme auftritt undThreadAbortException
genau dann eintrifft, wenn derfinally
Block ausgeführt werden soll, wird die Sperre nicht aufgehoben. Wie Sie bereits erwähnt haben, wird derlock() { }
Block wie folgt kompiliert:Wenn ein anderer Thread
Thread.Abort()
innerhalb des generiertenfinally
Blocks aufruft , wird die Sperre möglicherweise nicht aufgehoben.quelle
lock()
aber ich kann keine Verwendung davon sehen ... also - wie ist das verbunden? Dies ist eine "falsche" Verwendung vonMonitor.Enter
undMonitor.Exit
(ohne die Verwendung vontry
undfinally
)Monitor.Enter
undMonitor.Exit
ohne ordnungsgemäße Verwendung vontry
undfinally
- Ihr Szenario sperrt ohnehin andere Threads, an denen möglicherweise festgehalten wird_lock
, sodass es ein Deadlock-Szenario gibt - nicht unbedingt einen Zombie-Thread ... Außerdem lösen Sie die Sperre nicht inMain
... aber hey ... vielleicht sperrt das OP wegen Deadlocking anstelle von Zombiethreads :)lock
oder richtigetry
/finally
-usageHier geht es nicht um Zombie-Threads, aber das Buch Effective C # enthält einen Abschnitt zur Implementierung von IDisposable (Punkt 17), in dem es um Zombie-Objekte geht, die ich für interessant hielt.
Ich empfehle, das Buch selbst zu lesen, aber das Wesentliche ist, dass Sie, wenn Sie eine Klasse haben, die entweder IDisposable implementiert oder einen Desctructor enthält, nur Ressourcen freigeben sollten. Wenn Sie hier andere Dinge tun, besteht die Möglichkeit, dass das Objekt nicht durch Müll gesammelt wird, sondern auch in keiner Weise zugänglich ist.
Es gibt ein ähnliches Beispiel wie unten:
Wenn der Destruktor für dieses Objekt aufgerufen wird, wird ein Verweis auf sich selbst in die globale Liste aufgenommen, was bedeutet, dass er während der gesamten Lebensdauer des Programms am Leben bleibt und im Speicher bleibt, jedoch nicht zugänglich ist. Dies kann dazu führen, dass Ressourcen (insbesondere nicht verwaltete Ressourcen) möglicherweise nicht vollständig freigegeben werden, was zu allen möglichen Problemen führen kann.
Ein vollständigeres Beispiel finden Sie unten. Bis die foreach-Schleife erreicht ist, befinden sich 150 Objekte in der Liste der Untoten, die jeweils ein Bild enthalten. Das Bild wurde jedoch einer GC unterzogen, und Sie erhalten eine Ausnahme, wenn Sie versuchen, es zu verwenden. In diesem Beispiel wird eine ArgumentException angezeigt (Parameter ist ungültig), wenn ich versuche, etwas mit dem Bild zu tun, unabhängig davon, ob ich es speichern oder sogar Abmessungen wie Höhe und Breite anzeigen möchte:
Ich bin mir wieder bewusst, dass Sie insbesondere nach Zombiethreads gefragt haben, aber der Fragentitel bezieht sich auf Zombies in .net, und ich wurde daran erinnert und dachte, andere könnten es interessant finden!
quelle
_undead
statisch sein?NullReferenceException
, weil ich das Gefühl habe, dass das fehlende Ding mehr an die Maschine als an die Anwendung gebunden sein muss. Ist das richtig?IDisposable
darum. Ich habe BedenkenIDisposable
wegen Finalisierern (Destruktoren), aber wenn ich ein Objekt habe , wird es nicht in die Finalizer-Warteschlange gestellt und riskiert dieses Zombieszenario. Diese Warnung betrifft Finalizer und sie können Dispose-Methoden aufrufen. Es gibt Beispiele, bei denenIDisposable
Typen ohne Finalizer verwendet werden. Die Stimmung sollte für die Bereinigung von Ressourcen sein, aber das könnte eine nicht triviale Bereinigung von Ressourcen sein. RX wird gültigIDisposable
zum Bereinigen von Abonnements verwendet und kann andere nachgelagerte Ressourcen aufrufen. (Ich habe übrigens auch nicht abgelehnt ...)Auf kritischen Systemen unter hoher Last ist das Schreiben von Code ohne Sperren vor allem aufgrund der Leistungsverbesserungen besser. Schauen Sie sich Dinge wie LMAX an und wie es "mechanische Sympathie" für großartige Diskussionen darüber nutzt. Sorgen Sie sich um Zombiethreads? Ich denke, das ist ein Randfall, der nur ein Fehler ist, der ausgebügelt werden muss, und kein Grund genug, ihn nicht zu verwenden
lock
.Klingt eher so, als ob Ihr Freund nur ausgefallen ist und mir sein Wissen über obskure exotische Terminologie zur Schau stellt! Während der gesamten Zeit, in der ich die Performance Labs bei Microsoft UK leitete, bin ich in .NET nie auf eine Instanz dieses Problems gestoßen.
quelle
lock
Aussage machen!lock
Blöcke in Ordnung. Ich würde es vermeiden, Komplexität zum Zwecke der Leistungsoptimierung einzuführen, bis Sie wissen, dass Sie dies benötigen.Ich bin damit einverstanden, dass "Zombie-Threads" existieren. Dies ist ein Begriff, der sich darauf bezieht, was mit Threads passiert, die über Ressourcen verfügen, die sie nicht loslassen und dennoch nicht vollständig sterben, daher der Name "Zombie" Erklärung dieser Überweisung ist ziemlich richtig auf dem Geld!
Ja, sie können auftreten. Es ist eine Referenz und wird von Windows tatsächlich als "Zombie" bezeichnet: MSDN verwendet das Wort "Zombie" für tote Prozesse / Threads
Häufig passiert es eine andere Geschichte und hängt von Ihren Codierungstechniken und -praktiken ab, wie für Sie, die Thread Locking mögen und es für eine Weile getan haben, würde ich mir nicht einmal Sorgen machen, dass Ihnen dieses Szenario passiert.
Und ja, wie @KevinPanko in den Kommentaren richtig erwähnt hat, stammen "Zombie-Threads" von Unix, weshalb sie in XCode-ObjectiveC verwendet und als "NSZombie" bezeichnet und zum Debuggen verwendet werden. Es verhält sich fast genauso ... Der einzige Unterschied besteht darin, dass ein Objekt, das hätte sterben sollen, anstelle des "Zombie-Threads", der ein potenzielles Problem in Ihrem Code darstellen könnte, zu einem "ZombieObject" zum Debuggen wird.
quelle
Ich kann leicht genug Zombiefäden machen.
Dadurch lecken die Gewindegriffe (für
Join()
). Für uns in der verwalteten Welt ist es nur ein weiterer Speicherverlust.Nun, ein Faden so zu töten, dass er tatsächlich Schlösser hält, ist ein Schmerz im Heck, aber möglich. Der andere
ExitThread()
macht den Job. Wie er herausfand, wurde das Dateihandle vom GC aufgeräumt,lock
ein Objekt um ein Objekt jedoch nicht. Aber warum würdest du das tun?quelle