八、MethodImplAttribute

  如果临界区是跨越整个方法的,也就是说,整个方法内部的代码都需要上锁的话,使用MethodImplAttribute属性会更简单一些。这样就不用在方法内部加锁了,只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,MehthodImpl和MethodImplOptions都在命名空间System.Runtime.CompilerServices 里面。但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。因此,使用上不太灵活。如果要提前释放锁,则应该使用Monitor或lock。我们来看一个例子:

复制代码

Code 
 [MethodImpl(MethodImplOptions.Synchronized)]
 public void DoSomeWorkSync()
 {
 Console.WriteLine( " DoSomeWorkSync() -- Lock held by Thread " + 
 Thread.CurrentThread.GetHashCode());
 Thread.Sleep( 1000 );
 Console.WriteLine( " DoSomeWorkSync() -- Lock released by Thread " + 
 Thread.CurrentThread.GetHashCode());
 }
 public void DoSomeWorkNoSync()
 {
 Console.WriteLine( " DoSomeWorkNoSync() -- Entered Thread is " + 
 Thread.CurrentThread.GetHashCode());
 Thread.Sleep( 1000 );
 Console.WriteLine( " DoSomeWorkNoSync() -- Leaving Thread is " + 
 Thread.CurrentThread.GetHashCode());
 }[STAThread]
 static void Main( string [] args)
 {
 MethodImplAttr testObj = new MethodImplAttr();
 Thread t1 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
 Thread t2 = new Thread( new ThreadStart(testObj.DoSomeWorkNoSync));
 t1.Start();
 t2.Start();
 Thread t3 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
 Thread t4 = new Thread( new ThreadStart(testObj.DoSomeWorkSync));
 t3.Start();
 t4.Start();Console.ReadLine(); 
 }

复制代码

这里,我们有两个方法,我们可以对比一下,一个是加了属性MethodImpl的DoSomeWorkSync(),一个是没加的DoSomeWorkNoSync()。在方法中Sleep(1000)是为了在第一个线程还在方法中时,第二个线程能够有足够的时间进来。对每个方法分别起了两个线程,我们先来看一下结果:

 

可以看出,对于线程1和2,也就是调用没有加属性的方法的线程,当线程2进入方法后,还没有离开,线程1有进来了,这就是说,方法没有同步。我们再来看看线程3和4,当线程3进来后,方法被锁,直到线程3释放了锁以后,线程4才进来。

  九、同步事件和等待句柄

  用lock和Monitor可以很好地起到线程同步的作用,但它们无法实现线程之间传递事件。如果要实现线程同步的同时,线程之间还要有交互,就要用到同步事件。同步事件是有两个状态(终止和非终止)的对象,它可以用来激活和挂起线程。

  同步事件有两种:AutoResetEvent和 ManualResetEvent。它们之间唯一不同的地方就是在激活线程之后,状态是否自动由终止变为非终止。AutoResetEvent自动变为非终止,就是说一个AutoResetEvent只能激活一个线程。而ManualResetEvent要等到它的Reset方法被调用,状态才变为非终止,在这之前,ManualResetEvent可以激活任意多个线程。

  可以调用WaitOne、WaitAny或WaitAll来使线程等待事件。它们之间的区别可以查看MSDN。当调用事件的 Set方法时,事件将变为终止状态,等待的线程被唤醒。

  来看一个例子,这个例子是MSDN上的。因为事件只用于一个线程的激活,所以使用 AutoResetEvent 或 ManualResetEvent 类都可以。

复制代码

Code
 static AutoResetEvent autoEvent;static void DoWork()
 {
 Console.WriteLine(" worker thread started, now waiting on event");
 autoEvent.WaitOne();
 Console.WriteLine(" worker thread reactivated, now exiting");
 }[STAThread]
 static void Main(string[] args)
 {
 autoEvent = new AutoResetEvent(false);Console.WriteLine("main thread starting worker thread");
 Thread t = new Thread(new ThreadStart(DoWork));
 t.Start();Console.WriteLine("main thrad sleeping for 1 second");
 Thread.Sleep(1000);Console.WriteLine("main thread signaling worker thread");
 autoEvent.Set();Console.ReadLine(); 
 }

复制代码

我们先来看一下输出:

在主函数中,首先创建一个AutoResetEvent的实例,参数false表示初始状态为非终止,如果是true的话,初始状态则为终止。然后创建并启动一个子线程,在子线程中,通过调用AutoResetEvent的WaitOne方法,使子线程等待指定事件的发生。然后主线程等待一秒后,调用AutoResetEvent的Set方法,使状态由非终止变为终止,重新激活子线程。

参考:

1/MSDN(http://msdn.microsoft.com/zh-cn/library/ms173179(VS.80).aspx )