zoukankan      html  css  js  c++  java
  • unix网络编程--锁(一)

      阅读了unix网络编程的卷二之后,看着里面的实例并且理解其原理算法,就将里面的C语言的锁API进行C++封装以供以后使用。实现的锁接口以及一些算法会封装到我的TimePass库中。我觉得应该就锁的问题写一个系列的博客。锁按照其作用域可以分为线程锁和进程锁; 按照其工作方式:又可以分为互斥锁,读写锁,文件锁。读写锁也是互斥,只是相对于读写锁来说更加精细,其分为读和写,读与读不会互斥,读和写会互斥,写与写也会互斥。文件锁有相对于读写锁来说更加精细了,对整个文件,可以分区段进行加锁。

    1 互斥锁的API

      (1) int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr *mutexattr);

      初始化mutex; pthreadmutexattrt里面的shared字段设置成PTHREAD_PROCESS_SHARE,可以通过在共享内存里面创建锁,实现进程间的互斥锁。

      (2) int pthread_mutex_destroy(pthread_mutext *mutex);

      销毁锁资源

      (3) int pthread_mutex_lock(pthread_mutext *mutex);

       获取锁的资源,如果已经有进程或者线程获取到了就阻塞掉

      (4) int pthread_mutex_unlock(pthread_mutext *mutex);

      释放所资源,在阻塞的进程中或者线程中,按照阻塞的队列顺序获取锁资源

      (5) int pthread_mutex_trylock(pthread_mutext *mutex);

      尝试获取所资源,如果恰巧所资源被占用,就会返回EBUSY的错误码; 其不会像pthread_mutex_lock阻塞掉。这个函数可以用来查看锁的占用状况。

      以上如果仅仅是实现互斥锁的封装已经够用了,互斥锁的类名为Mutex,其数据成员如下:

    class Mutex {
    public:
    //成员函数 …. private: pthread_mutex_t mutex_; }

      (7) int pthread_cond_init(pthread_condt *cond, pthread_cond_attrt * condattr);

      初始化条件资源

      (8) int pthread_cond_destroy(pthreadcondt *cond);

      销毁条件资源

      (9) int pthread_cond_wait(pthreadcondt *cond, pthreadmutext *mutex);

       将mutex与cond进行绑定,当执行wait的时候,首先对mutex进行解锁,然后再进入阻塞队列,直到pthreadcondsignal通知,才唤醒; 紧接着占用锁资源,才向下执行。

      (10)int pthreadcondsignal(pthreadcondt *cond);

       通过条件资源唤醒互斥锁

      以上通过这些函数可以实现条件锁:

    class CondMutex:public Mutex{
    public:
    //成员函数 …. private: pthread_cond_t cond_; }

    2 消费者和生产者的原理

      想象一下,在一条生产线上,多台机器来生产物品,一台机器来打包物品;这其中生产线就是一个队列,生产物品的机器就是生产者,打包物品的机器就是消费者。在计算机算法中,就生产者和消费者而言,生产物品的机器就是生产者,消费物品的机器是消费者。每个生产者分别生产完一次物品,停止下来,让消费者去消费物品直到消费者消费完所有物品,停下来;生产者再去继续生产,并且第一个生产者通知生产线上已经有物品了,让消费者继续消费。就这样不停的往返反复,生产者和消费者共同去维护一个队列,在这过程中,生产者和消费者能够达到并发。

      为什么不是多个生产者和多个消费者,这样子速度不是更快么?

      为了维持队列的顺序,生产者和生产者是互斥的,消费者与消费者是互斥的,唯有生产者和消费不互斥。多个生产者确实有它的优势,当只有一个生产者的时候,生产线上有了一个产品之后,生产线停止,让消费者去消费,这样子就完全没有达到消费者和生产者的并发,只是串行的。当生产线上的物品都消耗完了,一个消费者会解锁,并且进入等待队列,这时候可能是另一个在等待队列中的消费者获取到锁资源,造成这个消费者饥饿消费;其次当生产者生产了物品,去通知消费者消费,要一个一个的去通知,即使采用群体唤醒,消费者们是互斥的;况且当消费过程是一个简短的过程,一个线程进行消费和多线程加锁进行消费比起来并不会慢,反而加锁和解锁会造成性能消耗的。注意只有当消费过程是一个比较耗时的过程,才会考虑有没有加锁的必要。

       为什么多个生产者分别生产一次物品,就会停下来?

       毕竟生产者消费者问题并不会像富士康生产线那样,流水线上两端的工人步调一致,生产线两头的工人不需要互相通知。一个生产者生产玩一次物品之后,会有一个通知的过程,为了防止多个线程同时通知,造成紊乱,就对通知过程也加锁了,而这个锁就是消费用的锁。当通知过去的时候,消费者会消费完当前物品,直到释放锁(消费者消费的过程中一直加锁解锁,可以在解锁的时候,sleep一下,同时也在生产者的过程中sleep一下,可以防止过度的消费或者过度的生产,尽量让消费者和生产者步调一致,不过sleep的时间不容易把握)。

    3 消费者和生产者的实现

     1   //生产者线程函数
     2   void* produce (void* arg) {
     3       while (生产没结束) {
     4           produce_lock()
     5           if (生产结束) {
     6               produce_unlock()
     7               return NULL;
     8           }
     9           //往生产线上放数据
    10           begin_produce();
    11           produce_unlock();
    12           
    13           //这里的锁运行速度很快
    14           consume_lock();
    15           if (满足通知条件) {
    16               //通知消费者去生产
    17               pthread_cond_signal();
    18           consume_unlock()
    19       }
    20       
    21   }
    View Code

      这是生产线程,在producelock里面,我们还需要判断生产是否结束,是为了防止这样的情况发生,当多个生产者线程被阻塞了,有一个线程完成了所有的生产任务,如果不判断,其他的生产线程继续往下执行,直到这个此次循环结束为止,会超出生产的量。

     1   //消费者线程函数
     2   void* consume(void* arg) {
     3        while (生产没结束 || 生产线上还有数据) {
     4            consume_lock();
     5            while (等待条件) { //注意这个条件与上面的通知条件是相反的
     6               //等待生产者的通知
     7               consume_wait();
     8            }
     9            begin_consume()
    10            consume_unlock()
    11       }
    12   }
    View Code

      等待生产者通知之所以放到while里面,是为了防止consume_wait调用失败,造成饥饿消费.

      以上是用互斥锁实现生产者和消费者的思路。在这里为了方便使用,我实现了一个四个类,类图如下:

      

      以上这个UML图,是用linux下面的Dia画,其完全可以替代visio,而且支持将UML图片转化为各种文件,比如png。

      这个生产者和消费者的框架使用起来很简单,只需要实现一个类来继承ProduceConsume,实现其中的纯虚函数。然后启用线程,调用ThreadFuncLoader里面的Produce和Consume就OK了,使用者不用纠结锁的问题。

      在这里使用者所要实现的函数有:

      Produce对应上面的begin_produce

      Consume对应上面的begin_consume

      ProduceComplete对应上面的“生产结束”

      ConsumeComplete对应上面你的“生产线上没有了数据"

      Condition 对应上面的“通知条件”“等待条件”

     
  • 相关阅读:
    逻辑代码题:五个学生参加计算机比赛
    逻辑代码题:如果昨天是明天,那今天就是周五了
    面试题:二叉树的遍历
    面试题:判断链表是否存在环
    面试题:将字符串中的中英文分开显示
    N皇后
    2017<java技术>预备作业2计科陈刚
    transition+transform合并效果案例
    transition CSS3动画属性之过渡
    Less 编译的几种方式
  • 原文地址:https://www.cnblogs.com/wind-qu/p/3639323.html
Copyright © 2011-2022 走看看