class Program
{
static void LockTooMuch(object lock1, object lock2)
{
lock(lock1)
{
Thread.Sleep(1000);
lock(lock2);
}
}
static void Main()
{
object lock1 = new object();
object lock2 = new object();
new Thread(() => LockTooMuch(lock1, lock2)).Start();
lock(lock2)
{
Thread.Sleep(1000);
Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
if(Monitor.TryEnter(lock1, Timespan.FromSeconds(5)))
{
Console.WriteLine("Acquired a protected resource successfully");
}
else
{
Console.WriteLine("Timeout acquired a resource!");
}
}
new Thread(() => LockTooMuch(lock1, lock2)).Start();
Console.WriteLine("---------------------------------");
lock(lock2)
{
Console.WriteLine("This will be a deadloock");
Thread.Sleep(1000);
lock(lock1)
{
Console.WriteLine("Acquired a protected resource successfully");
}
}
}
}
工作原理
先看看LockTooMuch方法。在該方法中,我們先鎖定第一個對象,等待一秒後鎖定了第二個對象。然後在另一個線程中啓動該方法。最後嘗試在主線程中先後鎖定第二個和第一個對象。
如果像該示例的第二部分一樣使用lock關鍵字,將會造成死鎖。第一個線程保持對lock1對象的鎖定,等待直到lock2對象被釋放。主線程保持對lock2對象的鎖定並等待直到lock對象被釋放,但lock1對象永遠不會被釋放。
實際上lock關鍵字是Monitor類用例的一個語法糖。如果我們分解使用了lock關鍵字的代碼,將會看到它如下面代碼片段表示:
bool acquiredLock = false;
try
{
Monitor.Enter(lockObject, ref acquiredLock);
//Code that accesses resources that are protected by lock.
}
finally
{
if(acquiredLock)
{
Monitor.Exit(lockObject);
}
}
因此,我們可以直接使用Monitor類。其擁有TryEnter方法,該方法接受一個超時參數。如果在我們能夠獲取被lock保護的資源之前,超時參數過期,則該方法會返回false.