上篇博客讲了互斥量问题:http://blog.csdn.net/sayhello_world/article/details/67637858
就拿生产者-消费者为例,如果两个线程都有互斥锁,但是生产者生产的快,消费者消费的慢,这里我们会发现,消费者一直再消费此时生产者还没有产生东西。这就会导致消费者总是加锁进来再解锁出去。为了让两者同步,这里提出了条件变量。
条件变量是线程可用的另一种同步机制。互斥量用于上锁,条件变量则用于等待,并且条件变量总是需要与互斥量一起使用。
需要了解的函数:
初始化函数:
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_wait(pthread_cond_t*restrict cond, pthread_mutex_t *restrict mutex);
信号函数:
int pthread_cond_signal(pthread_cond_t*cond);
销毁函数:
int pthread_cond_destroy(pthread_cond_t*cond);
和Mutex的初始化和销毁类似,pthread_cond_init 函数初始化为Condition Variable,attr参数为NULL则表示缺省属性,pthread_cond_destroy函数销毁一个Condition Variable。如果ConditionVariable是静态分配的,也可以用宏定义PTHEAD_COND_INITIALIZER初始化,相当于 用pthread_cond_init函数初始化并且attr参数为NULL。
一个线程可以调用pthread_cond_wait在一个ConditionVariable上阻塞等待,这个函数做以下三步操作:
1. 释放Mutex
2. 阻塞等待
3. 当被唤醒时,重新获得Mutex并返回
基于此 我们实现生产者-消费者模式。
代码思想说明:
生产者生产数据插入链表 消费者从链表中读取数据,中间场所为链表。
当消费者读取数据的时候 他需要加锁解锁并且要等待生产者生产数据。
当生产者写入数据的时候 他应该向链表中插入元素并且给消费者一个信号。
321模型:
3种关系:生产者--- 生产者,消费者--- 消费者,生产者--- 消费者
2种角色:生产者,消费者
1种交易场所:链表
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<pthread.h>
typedef struct plist
{
struct plist* next;
int data;
}node_t,*node_p,**node_pp;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static node_p malloc_node(int d)
{
node_p _n = (node_p)malloc(sizeof(node_t));
if(_n == NULL)
{
perror("malloc\n");
exit(1);
}
_n -> data = d;
_n -> next = NULL;
return _n;
}
static is_empty(node_p p)
{
return p->next == NULL?1:0;
}
static delete_node(node_p p)
{
if(p != NULL)
{
free(p);
}
}
void init_list(node_pp p)
{
*p = malloc_node(0);
}
//头插
void push_list(node_p p,int data)
{
node_p cur = malloc_node(data);
cur ->next = p->next;
p->next = cur;
}
//头删
void pop_list(node_p p,int *d)
{
assert(p);
assert(d);
if(!is_empty(p))
{
node_p temp = p->next;
p->next = temp->next;
*d = temp->data;
delete_node(temp);
}
}
//销毁链表
void destroy_list(node_p p)
{
assert(p);
int data;
while(!is_empty(p))
{
pop_list(p,&data);
}
delete_node(p);
}
void show_list(node_p p)
{
assert(p);
while(p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
static void *consumer(void *arg)
{
node_p phead = (node_p)arg;
int data = 0;
for(;;)
{
pthread_mutex_lock(&lock);
//当链表为空的时候,消费者应该等待生产者。
while(is_empty(phead))
{
printf("consumer no data\n");
pthread_cond_wait(&cond,&lock);
}
pop_list(phead,&data);
printf("consumer done:....%d\n",data);
pthread_mutex_unlock(&lock);
}
return NULL;
}
static void *produce(void *arg)
{
node_p phead = (node_p)arg;
int data = 0;
for(;;)
{
pthread_mutex_lock(&lock);
data = rand()%1234;
push_list(phead,data);
printf("produce data %d\n",data);
pthread_mutex_unlock(&lock);
//这里的sleep是为了让两者不同步,一个快一个慢。
sleep(1);
pthread_cond_signal(&cond);
printf("call consumer. preduce success val = %d\n",data);
}
return NULL;
}
int main()
{
node_p phead = NULL;
init_list(&phead);
pthread_t p_produce;
pthread_t p_consumer;
pthread_create(&p_consumer,NULL,consumer,phead);
pthread_create(&p_produce,NULL,produce,phead);
pthread_join(p_consumer,NULL);
pthread_join(p_produce,NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
destroy_list(phead);
return 0;
}
结果两者实现同步: