Strange Behaviour Wenn exklusiv gesperrt Objekt ändern - Monitor.Enter (x)
-
06-07-2019 - |
Frage
Ich wollte sehen, was passiert, wenn Sie die Referenz eines ausschließlich durch Monitor.Enter () gesperrt Objekt ändern. Wie erwartet wurde ein SynchronizationLockException geworfen. Aber ich war überrascht, mehr Threads zu sehen, immer vorbei an dem Monitor, bevor die Ausnahme ausgelöst wurde.
Hier ist, was unterhalb der Code tut.
- erstellen und starten 100 Threads
- macht den Thread warten, bis ein Manual gesetzt.
- die Manual gesetzt -. Kinda wie die grüne Fahne an einem Indy-Rennen winken
- setzt eine exklusive Sperre (Monitor.Enter (x)) auf x
- Verweis der Änderung x.
An dieser Stelle ich eine Art von Ausnahme zu erwarten geworfen, aber das geschieht nicht, bis Monitor.Exit (x). Was wirklich seltsam ist, dass 10 bis 20 Fäden über die Sperre bekommen scheinen zu können, bevor die Ausnahme. Wie kommt das? Scheint nicht, wie es sollte. Natürlich die Referenz des ausschließlich gesperrte Objekts zu ändern ist ein No-No. Ich würde es nie in echtem Code tun, aber immer noch war ich überrascht, dass die andere Threads zu sehen, hinter dem Monitor zu bekommen. Ihre Gedanken?
using System;
using System.Threading;
namespace ThreadingPlayground
{
class Program
{
private int _value;
object x = new object();
ManualResetEvent _event = new ManualResetEvent(false);
static void Main()
{
Program p = new Program();
p.StartThreads();
Console.ReadLine();
}
private void StartThreads()
{
for(int i = 0;i<100;i++)
{
Thread t = new Thread(IncrementValue);
t.Start();
}
_event.Set();
}
private void IncrementValue()
{
WaitHandle.WaitAll(new WaitHandle[] {_event});
Monitor.Enter(x);
// Change the reference of the exclusively locked object. This
// allows other threads to enter, should this be possible?
x = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine(++_value);
// throws a SynchronizationLockException
// but not until 10 - 20 more lines are written
Monitor.Exit(x);
}
}
}
Lösung
Was Sie sehen, Verhalten wird erwartet. Es ist nichts Besonderes über die tatsächliche Variable verwendet einen Verweis in Monitor.Enter()
passieren. Ändern die Referenz sollte nicht andere Threads verhindert, dass eine exklusive Sperre zu erwerben, da die Variablen einen neuen Wert hat, und das Bezug ist nicht überall verriegelt.
Ihr Problem kommt mit dem Exit
, da der Thread ruft Exit
in nicht eine exklusive Sperre auf der Referenz haben wird übergeben. Ein anderer Thread auch eine Sperre auf sie haben, aber der Thread Sie in tut bist Ausführung nicht.
Dies ist, wie Sie wissen, ist, warum es ist immer am besten Ihre Schließanlage mit einer Variablen, deren Bezug zu tun, wird sich nie ändern. Wenn die Variable Ihre Ressource könnte, eine neue Referenz verwenden ändern.
Ist das klar genug?
Andere Tipps
‚x‘ ist eine Referenz auf ein Objekt; es ist nicht das Objekt selbst. Wenn Sie das tun
x = Thread.CurrentThread.ManagedThreadId.ToString();
Sie haben das gesperrte Objekt weggeworfen, die zuvor genannten X und hergestellt x zu einem anderen Objekt bezieht. Nun, wenn Sie tun
Monitor.Exit(x);
Sie erhalten die Ausnahme, da dieses Objekt in der Tat nicht gesperrt ist - das Objekt, das Sie Müll ist jetzt gesperrt vom Garbage Collector gesammelt wird
.Der Grund für dieses Verhalten ist, dass man den Wert von x hier ist zu ändern:
x = Thread.CurrentThread.ManagedThreadId.ToString();
Also, wenn Sie bekommen
Monitor.Exit(x)
Sie Lösen der Verriegelung mit einem anderen Objekt. Es ist, als ob Sie ein Vorhängeschloss mit einem Schlüssel setzen, und versuchen, das Schloss mit dem Schlüssel aus einem anderen Schloss zu entfernen.
Darüber hinaus Console.WriteLine ist eine kostspielige Anweisung im Vergleich zu dem anderen, so dass mehrere Threads erhalten den Monitor eingegeben werden, bevor einer von ihnen in der Nähe der Ziellinie wird.
Hier ist ein Beispiel Lauf:
Sie beginnen eine Reihe von Themen.
- Thread
T1
erwirbt das Schloss mit dem Objektx
. -
T2
versucht Sperre mit dem Objektx
zu erwerben. Dieser Thread ist kein Glück, wie wir es geht warten wissen, für immer. -
T1
Änderungenx
. Es ist jetzt ein neues Objekt. Ich nenne esx'1
. -
T3
versucht, die Sperre zu erwerben. Aber der Variablex
verweist das Objekt tatsächlichx'1
. Niemand hat sich aufx'1
gesperrt, so dass er geht. -
T3
Änderungenx
. Es ist jetzt ein neues Objekt namensx'3
. -
T1
schreibt an die Konsole. -
T3
schreibt an die Konsole. -
T1
versucht, das Schloss mit der variablenx
zu lösen, die auf das Objekt verweist ...x'3
. - Die
Monitor
Objekt sagt: „Hey, was denkst du, was du tun Sie nicht, dass die Sperre haben Essen Sie dieses Ausnahme kleine Sauger?!“ - Fin