unix网络编程--锁(一)

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

1 互斥锁的API

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

  初始化mutex; pthreadmutexattrt里面的shared字段设置成PTHREAD_PROCESS_SHARE,能够经过在共享内存里面建立锁,实现进程间的互斥锁。git

  (2) int pthread_mutex_destroy(pthread_mutext *mutex);github

  销毁锁资源算法

  (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 对应上面的“通知条件”“等待条件”

相关文章
相关标签/搜索