a) 使用Monitor类
Monitor类提供了锁定部分代码的简单机制,只要把受保护的代码包装在Monitor.Enter与Monitor.Exit代码块中就行了。Monitor.Enter方法与Monitor.Exit方法都有一个参数。
Monitor.Enter(object [obj]);Monitor.Exit(object [obj]).这个参数就是需要Monitor锁定的对象,它应该是一个引用类型,而不是值类型。
Monitor具有以下功能:(摘自MSDN)
它根据需要与某个对象相关联。
它是未绑定的,也就是说可以直接从任何上下文调用它。
不能创建Monitor类的实例。
例子6:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
public class MonitorClass6
{
protected static int m_iCounter = 0;
public void Increment()
{
m_iCounter++;
}
public int Counter
{
get
{
return m_iCounter;
}
}
}
public class test
{
public MonitorClass6 mc6 = new MonitorClass6();
public void increseMC6()
{
Monitor.Enter(mc6);//进入临界区
try
{
mc6.Increment();
Console.WriteLine("线程{0}的值为{1}",Thread.CurrentThread.Name,mc6.Counter);
}
finally
{
Monitor.Exit(mc6);//退出临界区
}
}
}
public class MainEntryPoint
{
public static test myTest = new test();
public static void Main ()
{
ThreadStart ts1 = new ThreadStart(execute);
Thread t1 = new Thread(ts1);
t1.Name = "Thread1";
ThreadStart ts2 = new ThreadStart(execute1);
Thread t2 = new Thread(ts2);
t2.Name = "Thread2";
t1.Start();
t2.Start();
Console.Read();
}
public static void execute()
{
for (int i = 0; i <5; i++)
{
myTest.increseMC6();
}
}
public static void execute1()
{
for (int i = 0; i < 5; i++)
{
myTest.increseMC6();
}
}
}
}
执行结果如下:
可以看到它没有经过顺序上的紊乱就达到10;如果我们把两个有带有Monitor注释掉,结果会变成下图所示(您机子上的结果也许顺序不一样):
可以看出,由于我们没有对临界资源加以控制,而形成了我们不需要的“脏”数据!
也许细心的你会发现这个Monitor类的形式与使用LOCK关键字基本一致,但我认为它比lock关键字强大一些,因为Monitor类还具有 TryEnter(试图获取指定对象的排他锁)、Wait(释放对象上的锁并阻止当前线程)、Pulse(能知等待队列中的线程锁定对象状态的更改)以及PulseAll(通知所有的等待线程的对象状态已改变)等方法。
l TtyEnter
其方法的重载:
Monitor.TryEnter(Object):试图获得指定对象的排他锁
Monitor.TryEnter(Object,Int32):在指定的毫秒数内尝试获取指定对象上的排他锁
Monitor.TryEnter(Object,TimeSpan):在指定的时间量内尝试获取指定对象上的排他锁
将例子6的increseMC6方法改为如下:
public void increseMC6()
{
if (Monitor.TryEnter(mc6))//进入临界区
{
try
{
mc6.Increment();
Console.WriteLine("线程{0}的值为{1}", Thread.CurrentThread.Name, mc6.Counter);
}
finally
{
Monitor.Exit(mc6);//退出临界区
}
}
else
{
Console.WriteLine("线程{0}没有竞得资源",Thread.CurrentThread.Name);
}
}
}
l Wait
其方法的重载:
Monitor.Wait(Object):释放对象上的锁并阻止当前线程,直到它重新获取该锁
Monitor.Wait(Object,Int32):释放对象上的锁并阻止当前线程,直到它重新获取该锁。如果指定的超时间隔已过,则线程进入就绪队列。
Monitor.Wait(Object,TimeSpan):同上。
Monitor.Wait(Object,Int32,Boolean):前两个与第二个一样。其中的Boolean表示是否在等待之前退出上下文的同步域然后重新获取该同步域。
Monitor.Wait(Object,TimeSpan,Boolean):同上。
例程7:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
public class Monitor7
{
protected int m_iCounter = 0;
public void Increment()
{
m_iCounter++;
}
public int Counter
{
get
{
return m_iCounter;
}
}
}
public class MonitorPulseClass
{
protected Monitor 7 m _protectedResource = new Monitor7();
protected void ThreadOneMethod()
{
for (int i = 0; i < 5; i++)
{
lock (m_protectedResource)
{
Console.WriteLine("进入第一个线程,I的值为:{0}",i);
Monitor.Wait(m_protectedResource);
m_protectedResource.Increment();
int iValue = m_protectedResource.Counter;
Console.WriteLine("{Thread One} - Current value of counter:" + iValue.ToString());
}
}
}
protected void ThreadTwoMethod()
{
for (int i = 0; i < 5; i++)
{
lock (m_protectedResource)
{
Console.WriteLine("进入第二个线程,I的值为:{0}",i);
int iValue = m_protectedResource.Counter;
Console.WriteLine("{Thread Two} - Current value of counter" + iValue.ToString());
Monitor.PulseAll(m_protectedResource);
}
}
}
static void Main ()
{
MonitorPulseClass exampleClass = new MonitorPulseClass();
Thread threadOne = new Thread(new ThreadStart(exampleClass.ThreadOneMethod));
Thread threadTwo = new Thread(new ThreadStart(exampleClass.ThreadTwoMethod));
threadOne.Start();
threadTwo.Start();
System.Console.Read();
}
}
}
执行结果如下:由此可以得到流程如下:首先进入thread1然后被Monitor.Wait中断,从而进入thread2,在thread2中结束之后,通过Monitor.PulseAll通知thread1并执行,但又被Monitor.wait中断退出。