其实两者的功能都是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。Monitor.TryEnter方法就是获取对象的排它锁是否成功(方法具体版本可以参考MSDN);lock语句就是Monitor.Enter和Monitor.Exit的封装。MSDN里描述Monitor.TryEnter无论有没有获取对象锁都会返回结果,而Lock会在外面等直到占用的线程退出下一个线程才能进去。首先用代码测试两者的效果:
class Class1
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: 在此处添加代码以启动应用程序
//
for(int k =0;k<100;k++)
{
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(SetAdd));
thread.Start();
}
System.Threading.Thread.Sleep(40000);
Console.Write(i);
Console.Read();
}
static int i = 0;
private static object _LockObj = new object();
public static void Add()
{
if(System.Threading.Monitor.TryEnter(_LockObj))//lock(typeof(this))
{
try
{
i++;
System.Threading.Thread.Sleep(1);
}
finally
{
System.Threading.Monitor.Exit(_LockObj);
}
}
}
private static void SetAdd()
{
for(int i =0;i<100;i++)
{
Add();
}
}
}
上面的代码在运行的结果Monitor.TryEnter语句下i并不能完成所有数的累加,如果用lock结果i完成所有数的累加。正如MSDN所说Monitor.TryEnter在没有锁对象的情况下进行等待,而lock就处于等待直到完成。在执行过程方法或对象是所有线程唯的,那只好用lock;如果线程对锁操作可以弃或者有别的选择的情况可以采用Monitor.TryEnte;例如自己建立连接对象缓冲池就是线程在锁的情况下有多个选择。
从缓冲池获取连接对象首先是判连接对象是否可用,如果可用的情况下就进入对象锁操作方法获取对象的排它锁,在锁定对象后要处理一些东西如把连接对象的状态改变,标识一下个线程不能获取这个对象命用权并返回可用的连接对象等操作。如果我们用lock语来上锁,那么当一个线程在锁定对象后在作别的操作同时其他线程是在等待的;例如有两个线程X和Y,X先进入Connection1锁方法的临界区代码,那么Y就必须在外面等待其实这种等待是没意义的;因为Connection1已经被使用了在没有释的情况下Y线程进入锁操作的方法也是不允许获取这个对象使用权的。
Public bool Lock()
{
lock(this)
{
if(!IsLock)
{
………..
………..
IsLock =true;
Return true;
}
return false;
}
}
其实Y线程根本不应该等待,当Y不能进入Connection1锁操作方法时就应该马上放弃等待,对下一个对象进行请求。
Public bool Lock()
{
Monitor.TryEnter(this)
{
try
{
if(!IsLock)
{
………..
………..
IsLock =true;
Return true;
}
return false;
}
finally
{
Monitor.Exit(this);
}
}
}
Monitor.TryEnte提供灵活锁的方案,也可以很好地解决死锁等问题。