现在的位置: 首页 > 综合 > 正文

C# 温故而知新: 线程篇(四)

2012年02月11日 ⁄ 综合 ⁄ 共 4054字 ⁄ 字号 评论关闭

C# 温故而知新: 线程篇(四)

线程同步篇 (中):同步工具类的介绍

 

 

1 上篇回顾

    很抱歉好久没写博客了,由于工作太忙,所以最近一段时间落下了,让我们开始上一篇向大家介绍了下线程同步中的一些重要概念包括

基元内核模式,基元用户模式,原子性,然后由陆续介绍了基元用户模式中的Validated,Interloced 和ReaderWriterLock 类,同时也简单

介绍了下基元内核模式中的lock关键字,本章再让我们继续深入了解其他的一些同步工具类

 

2 继续介绍基元内核模式中的Monitor类

首先上图让大家有个初步的印象:

Monitor类也是同步机制中比较重要的一个类,它属于基元内核模式中的一种,也是上一章中与lock关键字有着密切关系,Monitor类采取

排他锁来进行对共享区的同步,当一个线程进入共享区时,会取得排他锁的控制权,其他线程则必须等待,大伙注意,这里有2个重要的线

程状态需要在说明下

1:等待队列

  等待进入共享区的线程会首先进入到等待队列中,等待持有排他锁的线程通知某个等待线程进入到就绪队列中,注意(只有拥

有排他锁的线程才能进行互换通知功能,甚至该线程能够唤醒一堆的等待线程进入到就绪队列中)

2:就绪队列

  等待队列中的某个线程被持有排他锁的线程唤醒放入到就绪队列中,等待获取排他锁的机会,这样一个周期便可以连接起来,

线程从等待到被唤醒到就绪状态,然后获取排他锁进如共享区操作,然后交出排他锁等待或者睡眠,直到再次被唤醒。

在这里强调下Monitor是个十分容易产生死锁的同步类,其原因是:

  1.当一个线程试图去请求锁对象时,它不是处在等待队列,而是就绪队列,如果需要让其进入等待队列,则必须使用Wait方法

  2.当一个线程释放锁对象时是不会通知等待队列中的线程进入到就绪队列,需要通过Palse方法

  3.线程启动的时候,线程是处于就绪状态的

  4.就算处于就绪状态的线程被cpu选中,但是一旦数据被锁定,那个线程还是无法获取到控制权

 

其实大家了解原因后对于monitor已经算了解,这正是monitor的机制

了解了上述机制后,大家可以开始理解该类比较重要的几个方法:

 Monitor. Enter(Object);  

 该方法旨在宣布当前线程进入了临界区,持有了排他锁,其他线程继续等待,直到该线程离开共享区交出排他锁给就绪队列中的一个线程

 Monitor. Exit(Object);     

 当持有共享锁的线程执行完任务之后,该线程变通过这个方法离开共享区,临走前可以操作其唤醒一个或一堆的等待线程

Monitor.Palse(Object)和Monitor.Palse(Object)

这两个方法比较复杂和相似,也就是唤醒(改变)其他线程状态的方法,持有排他锁的线程利用这两个方法通知其他线程进入到就绪队列,离开等待队列

Monitor.Wait(Object)

这个方法也是非常的重要,假如在共享区内的线程执行了wait方法后,该线程会被放入等待队列中,从而失去了排他锁的控制权,就绪队列中的下一个线程就进入到了临界区

Monitor.TryEnter(Object, Boolean))

有时其他线程希望通过进行尝试的方式主动去争取排他锁的控制权,这个方法便能实现这个功能,同时通过一个BOOL参数来指示会否占有了排他锁

文字介绍为了让大伙更好的理解Monitor类的本质,接下来就上很简单代码让我们更深入的了解

        /// <summary>
        /// 开启2个写线程,2个读线程演示
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Thread t1 = new Thread(new ThreadStart(WriteInShareArea));
            Thread t3 = new Thread(new ThreadStart(WriteInShareArea));
            Thread t2 = new Thread(new ThreadStart(Read));
            Thread t4= new Thread(new ThreadStart(Read));
            Console.WriteLine("t1's id={0}", t1.ManagedThreadId);
            Console.WriteLine("t2's id={0}", t2.ManagedThreadId);
            Console.WriteLine("t3's id={0}", t3.ManagedThreadId);
            t1.Start();
            t3.Start();
            t2.Start();
            t4.Start();
            Console.Read();
        }

        /// <summary>
        /// 读数据,首先是先让读线程等待,当写线程写完执行 Pulse 方法后,唤醒读线程继续工作
        /// </summary>
        private static void Read()
        {
            while (true)
            {
                Monitor.Enter(lockObj);
//读线程启动时默认就绪队列
//读线程执行到这里时会挂起进入等待队列 Monitor.Wait(lockObj, 4000); Console.WriteLine("Thread{0} can read nao", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Thread{0} reading...............", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); Monitor.Exit(lockObj); } } /// <summary> /// 写数据,当写线程写完执行 Pulse 方法后,唤醒读线程继续工作 /// </summary> private static void WriteInShareArea() { while (true) { Thread.Sleep(1000); Monitor.Enter(lockObj); Console.WriteLine("Thread{0} change data nao", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Thread{0} changing...............", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); //唤醒读线程进入就绪队列 Monitor.Pulse(lockObj); Monitor.Exit(lockObj); } }

执行结果:

 

 

 3  同步句柄:WaitHandle

      WaitHandle可以说是本章中许多同步对象的基类包括前后文中的AutoResetEventManualResetEventmutexSemaphore都是其子类,

waitHandle是个抽象类,主要的功能从字面上就能看的出来,等待句柄,不错 WaitHandle非常的神奇,它容纳了一个WIN32的内核对象句柄,线程

会等待内核对象的消息,当然内核对象也会等待接收信号,一旦接收到信号则会通知当前线程,这是一种复杂的操作系统调度原理,大家可以参考

WINDOWS核心编程等书继续深入了解下,本文旨在说明下waitHandle的概念和一些简单的介绍,这里在为waitHandle打个比方: 

       当你想穿过马路时,如果信号灯为红色则你只能等待,如果转变成绿灯的话,你就可以通行,其实waitHandle的作用就是红绿灯的作用,聪明的的你

肯定想到了,那我们可以将线程同步信号灯放入线程池中么?哈哈当然可以,不仅可以放一根,而且可以放入一堆WaitHadle对象。言归正传:WaitHadle

也有2个信号” Signaled” and “NonSignaled” ,前者可以理解为绿灯,绿灯状态时WaitOne方法无效,当前线程不会被阻止,后者可以理解为红灯,当底

层内核对象接受到信号时,通知某个当前线程可以进入到共享区(临界区)其余的必须等待,不过基于抽象类的考虑,waitHandle没有唤醒线程的set方法

而让其子类实现了,所以WaitHandle 还是完全体现其”等啊等,直到提示可以通过或者直接奔溃(超时异常)”的特色

   既然waitHandle只有阻塞等待的作用,那我们来看下它的几个奇妙的方法:

   1 bool WaitOne(): 等待一个通过当前waitHandle指定的内核对象收到信号后返回true,否则返回false

   2 bool WaitAll():等待waitHandle[]中所有的内核对象都收到信号后返回true,否则返回false

   3 bool WaitAny();等待waitHandle[]中内核对象都收到信号后返回true,否则返回false

   4 bool SignalAndWait():这个方法无法用文字表达清楚,但是大伙先可以理解成这样:自动的向内核对象发送信息,等待另一个内核对象接收到信号,

如果另一个内核对象接受到信号则返回TRUE,这牵涉到了复杂的”混合锁”的机制,所以本文不再详细说明,后章会详细介绍混合锁机制

接着让我们来看下WaitHandle的几个派生类结构,后文中将一一介绍:

EventWaitHandle

      AutoResetEvent

      ManualResetEvent

Mutex

Semaphore (将在下章详细介绍

最后上一个图来表示下WaitHandle的工作原理

 

抱歉!评论已关闭.