Monitor Class 在给定的时间和指定的代码段只能被一个线程访问,Monitor 类非常适合于这种情况的线程同步。这个类中的方法都是静态的,所以不需要实例化这个类。下面一些静态的方法提供了一种机制用来同步对象的访问从而避免死锁和维护数据的一致性。 Monitor.Enter 方法:在指定对象上获取排他锁。 Monitor.TryEnter 方法:试图获取指定对象的排他锁。 Monitor.Exit 方法:释放指定对象上的排他锁。 Monitor.Wait 方法:释放对象上的锁并阻塞当前线程,直到它重新获取该锁。 Monitor.Pulse 方法:通知等待队列中的线程锁定对象状态的更改。 Monitor.PulseAll 方法:通知所有的等待线程对象状态的更改。 通过对指定对象的加锁和解锁可以同步代码段的访问。Monitor.Enter, Monitor.TryEnter 和 Monitor.Exit用来对指定对象的加锁和解锁。一旦获取(调用了Monitor.Enter)指定对象(代码段)的锁,其他的线程都不能获取该锁。举个例子来说吧,线程X获得了一个对象锁,这个对象锁可以释放的(调用Monitor.Exit(object) or Monitor.Wait)。当这个对象锁被释放后,Monitor.Pulse方法和 Monitor.PulseAll方法通知就绪队列的下一个线程进行和其他所有就绪队列的线程将有机会获取排他锁。线程X释放了锁而线程Y获得了锁,同时调用Monitor.Wait的线程X进入等待队列。当从当前锁定对象的线程(线程Y)受到了Pulse或PulseAll,等待队列的线程就进入就绪队列。线程X重新得到对象锁时,Monitor.Wait才返回。如果拥有锁的线程(线程Y)不调用Pulse或PulseAll,方法可能被不确定的锁定。Pulse, PulseAll and Wait必须是被同步的代码段鄂被调用。对每一个同步的对象,你需要有当前拥有锁的线程的指针,就绪队列和等待队列(包含需要被通知锁定对象的状态变化的线程)的指针。 你也许会问,当两个线程同时调用Monitor.Enter会发生什么事情?无论这两个线程地调用Monitor.Enter是多么地接近,实际上肯定有一个在前,一个在后,因此永远只会有一个获得对象锁。既然Monitor.Enter是原子操作,那么CPU是不可能偏好一个线程而不喜欢另外一个线程的。为了获取更好的性能,你应该延迟后一个线程的获取锁调用和立即释放前一个线程的对象锁。对于private和internal的对象,加锁是可行的,但是对于external对象有可能导致死锁,因为不相关的代码可能因为不同的目的而对同一个对象加锁。 如果你要对一段代码加锁,最好的是在try语句里面加入设置锁的语句,而将Monitor.Exit放在finally语句里面。对于整个代码段的加锁,你可以使用MethodImplAttribute(在System.Runtime.CompilerServices命名空间)类在其构造器中设置同步值。这是一种可以替代的方法,当加锁的方法返回时,锁也就被释放了。如果需要要很快释放锁,你可以使用Monitor类和C# lock的声明代替上述的方法。 让我们来看一段使用Monitor类的代码: public void some_method() 上面的代码运行会产生问题。当代码运行到int c=a/b; 的时候,会抛出一个异常,Monitor.Exit将不会返回。因此这段程序将挂起,其他的线程也将得不到锁。有两种方法可以解决上面的问题。第一个方法是:将代码放入try…finally内,在finally调用Monitor.Exit,这样的话最后一定会释放锁。第二种方法是:利用C#的lock()方法。调用这个方法和调用Monitoy.Enter的作用效果是一样的。但是这种方法一旦代码执行超出范围,释放锁将不会自动的发生。见下面的代码: public void some_method() C# lock申明提供了与Monitoy.Enter和Monitoy.Exit同样的功能,这种方法用在你的代码段不能被其他独立的线程中断的情况。 |
正在阅读:c#.net多线程编程教学(3):线程同步c#.net多线程编程教学(3):线程同步
2005-07-07 10:44
出处:
责任编辑:moningfeng
键盘也能翻页,试试“← →”键