Другой конкретный пример:
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;
//Holding lock from _outside_ the class
IAsyncResult async;
lock (test)
{
//Calling method on another thread.
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);
}
}
В этом примере первый вызов будет успешным, но если вы отследите в отладчике, вызов DoSomething будет блокироваться до снятия блокировки. Второй вызов будет заблокирован, поскольку основной поток удерживает блокировку монитора на test .
Проблема в том, что Main может блокировать экземпляр объекта, что означает, что он может удерживать экземпляр от выполнения всего, что объект считает синхронизированным. Дело в том, что сам объект знает, что требует блокировки, а внешние помехи просто напрашиваются на неприятности. Вот почему шаблон с закрытой переменной-членом, которую вы можете использовать исключительно для синхронизации, не беспокоясь о внешних помехах.
То же самое относится и к эквивалентному статическому шаблону:
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;
//Holding lock from _outside_ the class
IAsyncResult async;
lock (typeof(Test))
{
//Calling method on another thread.
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);
}
}
Для синхронизации используйте частный статический объект, а не тип.