Wenn ich in der ac # threading-App ein Objekt sperren würde, sagen wir eine Warteschlange, und wenn eine Ausnahme auftritt, bleibt das Objekt gesperrt? Hier ist der Pseudocode:
int ii;
lock(MyQueue)
{
MyClass LclClass = (MyClass)MyQueue.Dequeue();
try
{
ii = int.parse(LclClass.SomeString);
}
catch
{
MessageBox.Show("Error parsing string");
}
}
So wie ich es verstehe, wird Code nach dem Fang nicht ausgeführt - aber ich habe mich gefragt, ob die Sperre freigegeben wird.
Antworten:
Zuerst; Haben Sie TryParse in Betracht gezogen?
Die Sperre wird aus zwei Gründen aufgehoben. Erstens
lock
ist im Wesentlichen:Zweite; Sie fangen die innere Ausnahme ab und werfen sie nicht erneut aus, sodass sie
lock
nie eine Ausnahme sieht. Natürlich halten Sie die Sperre für die Dauer einer MessageBox, was möglicherweise ein Problem darstellt.Es wird also bis auf die tödlichsten katastrophalen, nicht behebbaren Ausnahmen veröffentlicht.
quelle
TheadAbortException
zwischen demMonitor.Enter
und auftritttry
: blogs.msdn.com/ericlippert/archive/2009/03/06/…Ich stelle fest, dass niemand in seinen Antworten auf diese alte Frage erwähnt hat, dass das Aufheben einer Sperre für eine Ausnahme eine unglaublich gefährliche Sache ist. Ja, Sperranweisungen in C # haben "endlich" Semantik. Wenn die Steuerung das Schloss normal oder abnormal verlässt, wird das Schloss freigegeben. Sie reden alle darüber, als wäre es eine gute Sache, aber es ist eine schlechte Sache! Wenn Sie einen gesperrten Bereich haben, der eine nicht behandelte Ausnahme auslöst, ist es richtig, den erkrankten Prozess unmittelbar vor der Zerstörung weiterer Benutzerdaten zu beenden , die Sperre nicht freizugeben und fortzufahren .
Betrachten Sie es so: Angenommen, Sie haben ein Badezimmer mit einem Schloss an der Tür und eine Reihe von Leuten, die draußen warten. Eine Bombe im Badezimmer geht hoch und tötet die Person dort. Ihre Frage lautet: "Wird in dieser Situation das Schloss automatisch entriegelt, damit die nächste Person ins Badezimmer gelangen kann?" Ja, es wird. Das ist keine gute Sache. Dort ist gerade eine Bombe hochgegangen und hat jemanden getötet! Die Wasserleitungen sind wahrscheinlich zerstört, das Haus ist nicht mehr baulich einwandfrei und es könnte eine weitere Bombe darin sein . Das Richtige ist , alle so schnell wie möglich rauszuholen und das ganze Haus abzureißen.
Ich meine, denken Sie darüber nach: Wenn Sie einen Codebereich gesperrt haben, um aus einer Datenstruktur zu lesen, ohne dass dieser in einem anderen Thread mutiert ist, und etwas in dieser Datenstruktur eine Ausnahme ausgelöst hat, stehen die Chancen gut, dass dies an der Datenstruktur liegt ist korrupt . Benutzerdaten sind jetzt durcheinander; Sie möchten zu diesem Zeitpunkt nicht versuchen, Benutzerdaten zu speichern , da Sie dann beschädigte Daten speichern. Beenden Sie einfach den Vorgang.
Wenn Sie einen Codebereich gesperrt haben, um eine Mutation durchzuführen, ohne dass ein anderer Thread gleichzeitig den Status liest, und die Mutation ausgelöst wird, ist dies jetzt sicher , wenn die Daten zuvor nicht beschädigt waren . Genau vor diesem Szenario soll das Schloss schützen . Jetzt erhält Code, der darauf wartet, diesen Status zu lesen, sofort Zugriff auf den beschädigten Status und stürzt wahrscheinlich selbst ab. Auch hier ist es richtig, den Prozess zu beenden.
Unabhängig davon, wie Sie es in Scheiben schneiden, ist eine Ausnahme innerhalb eines Schlosses eine schlechte Nachricht . Die richtige Frage lautet nicht: "Wird mein Schloss im Falle einer Ausnahme bereinigt?" Die richtige Frage lautet: "Wie stelle ich sicher, dass es innerhalb eines Schlosses keine Ausnahme gibt? Und wenn ja, wie strukturiere ich mein Programm so, dass Mutationen auf frühere gute Zustände zurückgesetzt werden?"
quelle
ja, das wird richtig freigegeben;
lock
fungiert alstry
/finally
, mit demMonitor.Exit(myLock)
in der endgültigen, also egal wie Sie beenden, wird es freigegeben. Als Randnotizcatch(... e) {throw e;}
wird am besten vermieden, da dies die Stack-Trace beschädigte
; Es ist besser, es überhaupt nicht zu fangen , oder alternativ: Verwenden Sie es,throw;
anstattthrow e;
es erneut zu werfen.Wenn Sie es wirklich wissen möchten, lautet eine Sperre in C # 4 / .NET 4:
quelle
"Eine lock-Anweisung wird zu einem Aufruf von Monitor.Enter und anschließend zu einem try ... finally-Block kompiliert. Im finally-Block wird Monitor.Exit aufgerufen.
Die JIT-Codegenerierung für x86 und x64 stellt sicher, dass zwischen einem Monitor.Enter-Aufruf und einem unmittelbar darauf folgenden try-Block kein Thread-Abbruch auftreten kann. "
Entnommen aus: Diese Seite
quelle
Monitor.Enter
und dem einfügttry
, sodass die Bedingung "unmittelbar folgt" des JIT wird verletzt.Ihr Schloss wird ordnungsgemäß freigegeben. A
lock
verhält sich so:Und
finally
Blöcke werden garantiert ausgeführt, egal wie Sie dentry
Block verlassen .quelle
Nur um Marc's exzellenter Antwort ein wenig hinzuzufügen.
Situationen wie diese sind der eigentliche Grund für die Existenz des
lock
Schlüsselworts. Es hilft Entwicklern sicherzustellen, dass die Sperre imfinally
Block freigegeben wird .Wenn Sie gezwungen sind,
Monitor.Enter
/Exit
z. B. zur Unterstützung eines Timeouts zu verwenden, müssen Sie sicherstellen, dass der Aufruf anMonitor.Exit
imfinally
Block erfolgt, um im Falle einer Ausnahme die ordnungsgemäße Freigabe der Sperre sicherzustellen.quelle