同步构造(下)
最近学习有点不走心啊,唉,不知道咋回事,接着写写昨天的没写完的内容吧,写的不好,大家大大指教.
.NET4.0提供了一个CountdownEvent类,主要是让你等待多个线程.考虑下这样的场景:
有一个任务,3个人去做,你需要等这三个人都做完了才能继续下一步操作.案例如下:
class ThreadTest { static CountdownEvent _countdown = new CountdownEvent(3); public static void MainThread() { new Thread(SayHello).Start("i am 1"); new Thread(SayHello).Start("i am 2"); new Thread(SayHello).Start("i am 3"); _countdown.Wait();//阻塞,直到Singal被调用三次 Console.WriteLine("所有的线程都已经运行完毕!"); } static void SayHello(object obj) { Thread.Sleep(2000); Console.WriteLine(obj); _countdown.Signal(); } }
注意在构造函数中我们传递了3,代表了我们要等待3个线程都结束.
如果你的应用中有大量的线程都在一个WaitHandle上花费了大量的时间,你可以通过线程池的ThreadPool.RegisterWaitForSingleObject方法来提高资源的利用率,这个方法接受一个委托,当这个WaitHandle调用Single方法后,方法的委托就会被执行了.
案例:
class ThreadTest { static ManualResetEvent _starter = new ManualResetEvent(false); public static void MainThread() { RegisteredWaitHandle reg = ThreadPool.RegisterWaitForSingleObject(_starter,Go,"some data",-1,true); //在_starter上等待执行Go方法,-1代表永远不会超时 //true代表只执行一遍,"some data"是传递的参数 Thread.Sleep(5000); Console.WriteLine("Single is working..."); _starter.Set();//唤醒等待的线程 Console.ReadLine(); reg.Unregister(_starter);//取消注册 } static void Go(object obj,bool timeOut) { Console.WriteLine("started - "+obj); Console.WriteLine("ThreadID: "+Thread.CurrentThread.ManagedThreadId); } }
通过集成ContextBoundObject类,并且加上Synchronization特性,CLR会自动的为你的操作加锁.案例:
//继承自ContextBoundObject,且加上Synchronization特性修饰 [Synchronization] public class AutoLock : ContextBoundObject { public void Demo() { Console.WriteLine("Start..."); Thread.Sleep(1000); Console.WriteLine("end"); } //主线程 public static void MainThread() { AutoLock safeInstance = new AutoLock(); new Thread(safeInstance.Demo).Start(); new Thread(safeInstance.Demo).Start(); safeInstance.Demo(); } }
如果你仔细看看输出的话,会发现输出很有意思.
CLR会确保一次只有一个线程可以执行safeInstance里面的代码,它会自动的创建一个同步对象,然后在每次调用方法或属性的时候都lock它,从这个角度来看safeInstance是一个同步上下文.
但是它是怎么工作的,在Synchronization特性和System.Runtime.Remoting.Context命名空间中存在着线索.
一个ContextBoundObject被认为是一个远程(“Remote”)对象,意味着所有的方法调用都可以被介入.当我们实例化AutoLock的时候,CLR实际尚返回了一个proxy对象,一个和AutoLock对象有着同样的方法,同样属性的proxy对象,在这里它扮演着中介的对象.这样就为自动加锁提供了介入的机会,在每一次方法调用上都会花费几微妙的时间来介入.
你可能认为下面的代码会和上面的一样,是一样的输出:
//继承自ContextBoundObject,且加上Synchronization特性修饰 [Synchronization] public class AutoLock : ContextBoundObject { public void Demo() { Console.WriteLine("Start..."); Thread.Sleep(1000); Console.WriteLine("end"); } //主线程 public static void MainThread() { AutoLock.RunTest(); } public void Test() { new Thread(Demo).Start(); new Thread(Demo).Start(); new Thread(Demo).Start(); Console.ReadLine(); } public static void RunTest() { new AutoLock().Test(); } }
分析:实际上我们会运行到CR方法这里,然后等待输入.
为啥?
因为CLR会确保一次只有一个线程能够执行AutoLock的代码,所以当主线程执行到CR方法的时候,就开始等待输入了.如果你按下Enter,代码就和上面的输出一样了.
一点简单的同步构造就差不多说到这里了,因为本屌才疏学浅,没啥文化,如有错误请指出.