先看一段代码:
class Program
{
static void Main(string[] args)
{
//开启两个线程
var threadOne = new Thread(new ThreadSample("ThreadOne").CountNumbersByLock);
threadOne.Start();
var threadTwo = new Thread(new ThreadSample("ThreadTwo").CountNumbersByLock);
threadTwo.Start();
//var threadOne = new Thread(new ThreadSample("ThreadOne").CountNumbersByMonitor);
//threadOne.Start();
//var threadTwo = new Thread(new ThreadSample("ThreadTwo").CountNumbersByMonitor);
//threadTwo.Start();
Console.ReadKey();
}
}
class ThreadSample
{
private readonly string _threadNo;
private readonly static object staticObject = new object();
public ThreadSample(string threadNo)
{
_threadNo = threadNo;
}
public void CountNumbersByLock()
{
Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
lock (staticObject)
{
Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine($"线程 {_threadNo} is count number..... ");
}
Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
}
public void CountNumbersByMonitor()
{
bool isRequiredLock = false;
try
{
Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
Monitor.Enter(staticObject, ref isRequiredLock);
Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine($"线程 {_threadNo} is count number..... ");
}
finally
{
if (isRequiredLock)
{
Monitor.Exit(staticObject);
Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
}
}
}
}
调用CountNumbersByLock结果:
调用CountNumbersByMonitor结果:
结果一样,为什么呢?实际上lock关键字是Monitor类用例的一个语法糖。lock的效果等同于
bool isRequiredLock = false;
try
{
Monitor.Enter(staticObject, ref isRequiredLock);
// code
}
finally
{
if (isRequiredLock)
{
Monitor.Exit(staticObject);
// code
}
}
接下来看一个死锁的例子:
class Program
{
static void Main(string[] args)
{
//开启两个线程
var threadOne = new Thread(new ThreadSample("ThreadOne").LockA);
threadOne.Start();
var threadTwo = new Thread(new ThreadSample("ThreadTwo").LockB);
threadTwo.Start();
Console.ReadKey();
}
}
class ThreadSample
{
private readonly string _threadNo;
private readonly static object staticObjectA = new object();
private readonly static object staticObjectB = new object();
public ThreadSample(string threadNo)
{
_threadNo = threadNo;
}
public void LockA()
{
Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
lock (staticObjectA)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
lock (staticObjectB)
{
Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine($"线程 {_threadNo} is count number..... ");
}
}
Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
}
public void LockB()
{
Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
lock (staticObjectB)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
lock (staticObjectA)
{
Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine($"线程 {_threadNo} is count number..... ");
}
}
Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
}
}
结果:
线程1和2一直处于等待中形成了死锁。
Monitor类中的TryEnter方法可以设置过期时间,避免死锁问题ThreadSample类修改代码如下:
class ThreadSample
{
private readonly string _threadNo;
private readonly static object staticObjectA = new object();
private readonly static object staticObjectB = new object();
public ThreadSample(string threadNo)
{
_threadNo = threadNo;
}
public void LockA()
{
Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
if(Monitor.TryEnter(staticObjectA,TimeSpan.FromSeconds(3)))
{
Thread.Sleep(TimeSpan.FromSeconds(2));
if (Monitor.TryEnter(staticObjectB, TimeSpan.FromSeconds(3)))
{
Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine($"线程 {_threadNo} is count number..... ");
}
}
Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
}
public void LockB()
{
Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
if (Monitor.TryEnter(staticObjectB, TimeSpan.FromSeconds(3)))
{
Thread.Sleep(TimeSpan.FromSeconds(2));
if (Monitor.TryEnter(staticObjectA, TimeSpan.FromSeconds(3)))
{
Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine($"线程 {_threadNo} is count number..... ");
}
}
Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
}
}
调试结果:
上面的代码设置了锁定超时时间为3秒,也就是说,在3秒中后,lockObj还未被解锁,TryEntry方法就会返回false,如果在3秒之内,lockObj被解锁,TryEntry返回true。我们可以使用这种方法来避免死锁