标签:线程
多线程编程在操作系统中是十分重要的。
而在线程中处理同步与互斥问题又是至关重要的。生产者-消费者模型,(也称有限缓冲问题)是一个多线程同步问题的经典例子。下来我们对其进行简单分析。
生产者——>生成一定量的数据放到缓冲区中,然后重复此过程;
消费者——>在缓冲区消耗这些数据。
而生产者-消费者之间存在三种关系,即
生产者与生产者之间是互斥关系;
消费者与消费者之间是互斥关系;
生产者与消费者之间是同步与互斥关系。
下面的程序演示了一个生产者-消费者的例子,生产者生产一个结构体串在链表的表头上,消费者从表头取走结构体。(基于互斥锁)
#include<stdio.h> #include<malloc.h> #include<pthread.h> typedef int _dataType_; typedef int* _dataType_p_; typedef struct _node { _dataType_ data; struct _node* next; }node,*nodep,**nodepp; nodep head=NULL; pthread_cond_t cond=PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; nodep buyNode(_dataType_ val) { nodep tmp=(nodep)malloc(sizeof(node)); if(tmp!=NULL) { tmp->data=val; tmp->next=NULL; return tmp; } return NULL; } void init(nodepp head) { *head=buyNode(0); } void push_list(nodep head,_dataType_ val) { nodep tmp=buyNode(val); tmp->next=head->next; head->next=tmp; } int pop_list(nodep head,_dataType_p_ pval) { if(head->next==NULL) return -1; nodep del=head->next; *pval=del->data; head->next=del->next; free(del); return 0; } void* product(void* arg) { _dataType_ i=0; while(1) { sleep(1); pthread_mutex_lock(&mutex); push_list(head,i++); pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); } pthread_exit((void*)1); } void* consumer(void* arg) { _dataType_ val=0; while(1) { sleep(1); pthread_mutex_lock(&mutex); if(pop_list(head,&val)==-1) pthread_cond_wait(&cond,&mutex); printf("data:%d\n",val); pthread_mutex_unlock(&mutex); } pthread_exit((void*)1); } int main() { pthread_t tid1,tid2; init(&head); pthread_create(&tid1,NULL,product,NULL); pthread_create(&tid2,NULL,consumer,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); free(head); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); return 0; }
上述的生产者-消费者的例子是基于链表的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序(使用POSIX信号量)。
它有以下特征:
生产者不能把消费者套圈、消费者不能超过生产者;
数据为空不消费;
数据为满不生产;
环形队列的基本模型如下:
实现如下:
#include<stdio.h> #include<pthread.h> #include<semaphore.h> #define _SEM_PRO_ 20 #define _SEM_COM_ 0 typedef int _dataType_; _dataType_ blank[_SEM_PRO_]; sem_t sem_product; sem_t sem_consumer; void* product(void* arg) { int index=0; int count=0; while(1) { sleep(rand()%5); sem_wait(&sem_product); blank[index++]=count++; sem_post(&sem_consumer); index%=_SEM_PRO_; } pthread_exit((void*)1); } void* consumer(void* arg) { int index=0; while(1) { sem_wait(&sem_consumer); printf("data:%d\n",blank[index++]); sem_post(&sem_product); index%=_SEM_PRO_; } pthread_exit((void*)1); } int main() { pthread_t tid1,tid2; sem_init(&sem_product,0,20); sem_init(&sem_consumer,0,0); pthread_create(&tid1,NULL,product,NULL); pthread_create(&tid2,NULL,consumer,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); sem_destroy(&sem_product); sem_destroy(&sem_consumer); return 0; }
生产者-消费者问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。 通常可以采用进程间通信的方法解决该问题。如果解决办法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
接下来讨论另一种经典例子,读者-写者模型,也就是读写锁。
读者-写者模式也存在三种关系两种方式一个场所,即:
三种关系:读者与读者:无关系;
写者与写者:互斥关系;
读者与写者:同步与互斥关系;
两种方式:读者优先(以读者优先) 、写者优先(以写者为核心)。
一个场所:同一临界资源(数据)。
自旋锁:如果所等待条件不满足,不挂起,一直申请锁。适宜某个线程占用锁时间较短。
当不断有多个读者准备读时,如果有写者到来,此时应该当前读者读完后,提高写者优先级,下个进入临界区的是写者。(写者优先,否则会造成写者饥饿)
当不断有多个写者准备写时,如果有读者到来,此时应该当前写者写完后,提高读者优先级,下个进入临界区的是读者。(读者优先,否则会造成读者饥饿)
代码如下:
#include<stdio.h> #include<pthread.h> int g_val=0; pthread_rwlock_t rw_lock; void* reader(void* arg) { while(1) { pthread_rwlock_rdlock(&rw_lock); printf("g_val: %d\n",g_val); pthread_rwlock_unlock(&rw_lock); } } void* writer(void* arg) { while(1) { sleep(1);//使写者隔一秒竞争一次锁资源 pthread_rwlock_wrlock(&rw_lock); g_val++; pthread_rwlock_unlock(&rw_lock); } } int main() { pthread_t tid1,tid2; pthread_rwlock_init(&rw_lock,NULL); pthread_create(&tid1,NULL,reader,NULL); pthread_create(&tid2,NULL,writer,NULL); pthread_rwlock_destroy(&rw_lock); pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
通过分析可以看出生产者-消费者模型与读者-写者模型之间的区别:
1.前者完全是生产与消费的关系,后者中的读者在充当消费者的基础上实际上也在变相生产,充当了不自觉的生产者,而且这种再生产将不会停止。
2.读者-写者问题是缓冲区只能有一个写者占用;而生产者-消费者问题是缓冲区存放的是多个产品,比如有物品消费者才能访问缓冲区。
本文出自 “七月朔风” 博客,请务必保留此出处http://luminous.blog.51cto.com/10797288/1827531
标签:线程
原文地址:http://luminous.blog.51cto.com/10797288/1827531