Ein weiteres konkretes Beispiel:
class Program
{
public class Test
{
public string DoThis()
{
lock (this)
{
return "got it!";
}
}
}
public delegate string Something();
static void Main(string[] args)
{
var test = new Test();
Something call = test.DoThis;
IAsyncResult async;
lock (test)
{
async = call.BeginInvoke(null, null);
}
async.AsyncWaitHandle.WaitOne();
string result = call.EndInvoke(async);
lock (test)
{
async = call.BeginInvoke(null, null);
async.AsyncWaitHandle.WaitOne();
}
result = call.EndInvoke(async);
}
}
In diesem Beispiel ist der erste Aufruf erfolgreich. Wenn Sie jedoch im Debugger nachverfolgen, wird der Aufruf von DoSomething blockiert, bis die Sperre aufgehoben wird. Der zweite Aufruf wird blockiert, da der Haupt-Thread die Monitorsperre beim Test hält .
Das Problem ist, dass Main die Objektinstanz sperren kann, was bedeutet, dass die Instanz alles verhindern kann, was das Objekt für synchronisiert hält. Der Punkt ist, dass das Objekt selbst weiß, was gesperrt werden muss, und dass Störungen von außen nur nach Problemen fragen. Aus diesem Grund das Muster einer privaten Mitgliedsvariablen, die Sie ausschließlich für die Synchronisierung verwenden können, ohne sich um Störungen von außen sorgen zu müssen.
Gleiches gilt für das äquivalente statische Muster:
class Program
{
public static class Test
{
public static string DoThis()
{
lock (typeof(Test))
{
return "got it!";
}
}
}
public delegate string Something();
static void Main(string[] args)
{
Something call =Test.DoThis;
IAsyncResult async;
lock (typeof(Test))
{
async = call.BeginInvoke(null, null);
}
async.AsyncWaitHandle.WaitOne();
string result = call.EndInvoke(async);
lock (typeof(Test))
{
async = call.BeginInvoke(null, null);
async.AsyncWaitHandle.WaitOne();
}
result = call.EndInvoke(async);
}
}
Verwenden Sie zum Synchronisieren ein privates statisches Objekt, nicht den Typ.