一、生产者-消费者问题:
同步和互斥是进程间很重要的交互模式,而生产者和消费者问题则是同步和互斥的一个经典模型。
同步是一种时序关系。如规定了进程1 处理完事情A后,进程2 才能处理事情 B,经典的同步问题是生产者和消费者间的同步。
互斥描述的是一种独占关系。如任一时刻,进城1 和进程2 中只能有一个写文件C
生产者-消费者问题:
1、在同一个进程地址空间内执行的两个线程生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。
2、生产者中先同步再互斥,而消费者先互斥再同步,或反之;以及生产者和消费者都先互斥再同步这几种情况都不会死锁,因为它们间并没有交叉关系,就更不可能形成死锁环。之所以先同步,再互斥,是为了更好的并发性:并发性的瓶颈是互斥区,先同步再互斥,使得互斥区代码更短。
二、条件变量
条件变量(Condition Variable):线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。
在pthread库中通过条件变量来阻塞等待一个条件,或者唤醒等待这个条件的线程。
Condition Variable 用pthread_cond_t类型的变量表示,可以这样初始化和销毁.
返回值:成功返回0,失败返回错误号。
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr); //初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//定义条件变量
int pthread_cond_destroy(pthread_cond_t *cond); //销毁条件变量
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime); //条件不成熟,等待
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_broadcast(pthread_cond_t *cond); //广播式唤醒
int pthread_cond_signal(pthread_cond_t *cond); //唤醒等待的线程
条件检测:pthtread_cond_wait() 放至while循环中,进行条件检测。
三、单 生产者-消费者模型:
输入时,输入线程(插入等)是生产者,而计算线程是消费者;
输出时,计算线程(删除等)是生产者,而打印线程是消费者。
//链表:头进 尾出
#include<stdio.h> #include<stdlib.h> #include<pthread.h> typedef int data_type; typedef int* data_type_p; typedef struct _node { data_type data; struct _node *next; }node_t,*node_p,**node_pp; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; node_p head=NULL; static node_p buy_node(data_type _data) { node_p tmp=malloc(sizeof(node_p)); if(tmp) { tmp->data=_data; tmp->next=NULL; return tmp; } return NULL; } void init_list(node_pp _phead) { *_phead=buy_node(0); } void delete_node(node_p tmp) { if(tmp) { free(tmp); tmp=NULL; } } int pop_node(node_p list,data_type_p _data_p) { if(list->next == NULL) { *_data_p=-1; return -1; } node_p tmp=list->next; list->next=tmp->next; *_data_p=tmp->data; delete_node(tmp); return 0; } void push_node(node_p list,data_type _data) { node_p tmp=buy_node(_data); tmp->next=list; list->next=tmp; } void show_list(node_p list) { node_p cur=list; while(cur) { printf("%d ",cur->data); fflush(stdout); cur=cur->next; } } void *product(void *arg) { int i=0; while(1) { pthread_mutex_lock(&lock); printf("product data:%d\n",i++); push_node(head,i++); printf("product done ... wakeup consumer\n"); pthread_mutex_unlock(&lock); pthread_cond_signal(&cond); sleep(1); } } void *consumer(void *arg) { data_type _data; while(1) { pthread_mutex_lock(&lock); while(-1 == pop_node(head,&_data)); { pthread_cond_wait(&cond,&lock); } printf("consumer data:%d\n",_data); pthread_mutex_unlock(&lock); sleep(1); } } int main() { pthread_cond_t cond; pthread_cond_init(&cond,NULL); pthread_t tid1,tid2; pthread_create(&tid1,NULL,product,NULL); pthread_create(&tid2,NULL,consumer,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_cond_destroy(&cond); return 0; }
//链表的更改:尾进 头出
... ... int pop_node(node_p list,data_type_p _data_p) { if(list->next == NULL) { *_data_p=-1; return -1; } node_p tmp=list; list=list->next; delete_node(tmp); return 0; } void push_node(node_p list,data_type _data) { node_p tmp=buy_node(_data); while(list->next) { list=list->next; } list->next=tmp; tmp->data=_data; tmp->next=NULL; } ... ...
四、多生产者-消费者模型:
// 其余代码见上 int main() { pthread_cond_t cond; pthread_cond_init(&cond,NULL); pthread_t tid1,tid2,tid3,tid4; pthread_create(&tid1,NULL,product,NULL); pthread_create(&tid2,NULL,product,NULL); pthread_create(&tid3,NULL,consumer,NULL); pthread_create(&tid4,NULL,consumer,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); pthread_join(tid4,NULL); pthread_cond_destroy(&cond); return 0; }
注意 :生产者消费者问题,是同步和互斥的一个经典模型。但需要注意死锁问题,需要先同步再互斥。