ICE中的Monitor类:一种有趣的实现多线程之间、生产者与消费者的大体思路

【应用背景】

         系统之上的应用软件进程中,各个线程实现多个线程生产消费者这样的模式,非常困难,有一个原因是线程是被系统调度的,因此对临界资源访问的发生时间在应用层看来是未知的,无法确定某一个生产者线程何时生产消息,消费者线程何时取消息也是不确定的,所以生产者消费者这样的关系比线程之间对资源的互斥访问要难的多,内核有调度器,系统内生产者生产完消息就能唤醒被挂起的进程。但是在应用层没有这样的功能,调度是系统决定的,所以怎么样唤醒等待的消费者线程就成了这个类的首要任务。根据ICE说明手册上的例子

【问题】

      按照思路生产者和消费者共同竞争消息池的互斥锁,生产者放入消息后会notify()唤醒等待的消费者(可能不只一个消费者线程),消费者进入消息池后首先是判断有消息就处理,否则wait()将自己挂起。因此关键是notify()怎么唤醒消费者线程。谁唤醒这些等待在_queue(Monitor类包含的cond类中的成员)上的线程,因为它们之所以等待是因为已经被挂起。其次消息池中有消息时,被唤醒的消费者线程需不需要再一次竞争消息池的互斥锁,或者下一个生产者也想获得这个锁。

【大致的实现】

       Monitor借助了_cond类中Semaphore这个数据结构,通过函数CreateSemaphore(0,0, 0x7fffffff,0)创建了一个有0个数目的信号_queue因此只要是要获取这个信号量的线程都会被挂起,1个数目的信号量_gate _gate.post()时每次只放一个线程进来,唤醒时只唤醒一个线程。并且定义了内部成员,生产者和消费者互斥访问消息池的互斥锁_mutex。

        Monitor维持一个整型的内部状态_nnotify,它的语义是表示消费者调用了多少次notify函数通知消费者线程消息池中有消息可以取,_nnotify等于0表示是在对_mutex加锁,为-1表示占有锁的生产者调用了notifyAll函数唤醒消费者线程,只要notifyAllnotify函数就不再起作用,否则每调用一次notify内部状态_nnotify就加1Wait函数返回后也将_nnotify置为0这样就形成了统一,无论是生产者还是消费者只要是获取Monitor_nnotify就会被置成0Monitor没有要求唤醒消费者线程的任务交给生产者,这是因为每一个消费者线程都会循环检测消息池中是否有有消息可取而循环调用wait函数。生产者线程如果唤醒多个消费者,带来的问题是,有一些消费者线程被唤醒之后发现消息已经被别的消费者取走,因此要把自己重新挂起到_queue信号上,生产者不可能做得到。

【经典】

      最有意思也是最经典的设计,将唤醒消费者的操作交给了消费者在wait函数里面自己去做,基于这样几点原因,消费者会循环检测消息池的状态并调用wait函数,消费者在被唤醒后需要重新获取消息池Monitor的锁_mutex。因此每一次循环调用wait之前都要检测_nnotify的值如果为0表示是正常的加锁情形,什么也不做一旦发现为-1或者是大于0就唤醒cond._queue上的所有挂起的线程,这时候其他的线程还不能获得_mutex,因为该线程还持有这个锁,紧接着该线程进入到cond类的代码区在prewait函数中获得了_gate信号量之后,它才将锁释放。问题接着出现了:这个本来只是为了检测消息池中的是否有消息而调用wait函数的线程,当有消息来临时还有必要将自己挂起到_queue信号上吗?ICE将它挂起了,它成了牺牲品并且是第一个告诉大家消息池有消息了的线程,它被其他线程唤醒时它再和其他线程竞争_mutex锁。

【总结】

       Cond类并不简单容易让人理解,RecMutex递归互斥锁中LockState记录了调用锁的线程递归进入临界区的次数,与后续存和恢复储锁的状态有关。Monitor是作为类模板实现的,作为模板参数的类_mutex当为RecMutex递归互斥锁实现时,循环调用wait对系统的开销非常小,因为实现RecMutex锁用临界区CRITICAL_SECTION来实现的,CRITICAL_SECTION似乎可以递归重入。虽然读到的代码非常少,仅仅是从看到的这几百行代码就知道一定是一个非常懂得系统实现的人写的,ICE真不简单。 

  

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值