pthread多线程学习笔记五条件变量1介绍

互斥量是用来上锁的。有时候我们需要这样一种情形:

线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒A继续执行。

使用互斥量固然可以,线程A不断的去尝试加锁,但却造成cpu周期的浪费,线程A的阻塞等待能是不耗费cpu周期的?

这就是引入条件变量的作用。

ß 条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

ß 条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

针对条件变量的这两个动作,就定义了条件变量响应的两个函数。

等待:

ß int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

ß int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex_t *mutex, const struct timespec *timeout)

第二个函数仅比第一个多了时间变量,如果给定时间内还未被唤醒,则返回ETIMEDOUT。注意时间是一个绝对值而不是一个相对值,具体使用可以先使用gettimeofday函数获得时间。

唤醒:

ß int pthread_cond_signal(pthread_cond_t *cond)

ß int pthread_cond_broadcast(pthread_cond_t *cond)

POSIX为了简化实现,允许pthread_cond_signal在实现的时候可以唤醒不止一个线程

条件变量的类型标识符是pthread_cond_t,与互斥量一样,需要初始化,用完后需要销毁。

ß 初始化:

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t*attr);

PTHREAD_COND_INITIALIZER

ß 销毁:int pthread_cond_destroy(pthread_cond_t *cond)

看一个例子,有点长,是生产者-消费者模型的例子。从一篇教程里找到的,尽量的完善了下,修改了其中可能引起问题的地方。

生产者和消费者都是单独的一个线程,生产者生产产品,消费者从中取出产品,产品是一个队列,生产者new一个节点添加进来,消费者取出一个节点并free掉,当消费者取的速度很快,而生产者生产的很慢时,消费者就进入休眠等等(wait),而当生产者生产产品后,就唤醒(signal)消费者队列。程序节点定义为一个结构体msg。

#include <stdlib.h>

#include <pthread.h>

#include <stdio.h>

struct msg {

struct msg *next;

int num;

};

volatile struct msg *head;

pthread_cond_t hasProduct = PTHREAD_COND_INITIALIZER;

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void *consumer(void *p)

{

struct msg *mp;

for(;;) {

pthread_mutex_lock(&lock);

while (head==NULL) {

/*if (head==NULL) {*/

//条件受互斥锁保护?

//用while不用if?

printf("consumer entering wait......\n");

pthread_cond_wait(&hasProduct,&lock);

printf("consumer leaveing wait : %d......\n",head->num);

}

mp = head;

head = mp->next;

printf("Consume %d\n",mp->num);

pthread_mutex_unlock(&lock);

free(mp);

sleep(rand()%5);

}

}

void *producer(void *p)

{

struct msg *mp;

for(;;) {

mp = malloc(sizeof(struct msg));

mp->num = rand()%1000 + 1;

pthread_mutex_lock(&lock);

mp->next = head;

head = mp;

printf("Produce %d\n",mp->num);

pthread_mutex_unlock(&lock);

pthread_cond_signal(&hasProduct);

//signal与lock位置无关

sleep(rand()%5);

}

}

int main(int argc, char *argv[])

{

pthread_t pid,cid;

srand(time(NULL));

pthread_create(&pid,NULL, producer, NULL);

pthread_create(&cid,NULL, consumer, NULL);

pthread_join(pid,NULL);

pthread_join(cid,NULL);

struct msg* mp = head;

while (head!=NULL) {

mp = head;

head = head->next;

printf("main thread: %d\n",mp->num);

free(mp);

}

return 0;

}

这个例子是多线程编程里比较经典的例子,刚开始学习条件变量时其实很多问题没弄明白。没关系,其实很多条件变量的例子形式都差不多是这个样子。关于我的疑问,先提出来,下篇笔记里一个一个记录下找到的和想到的答案。。。

1. consumer里为什么是while而不是if,要知道producer是改变了条件了才signal的。

2. wait一定要在lock之后,为什么用mutex去保护cond?

3. signal的位置和mutex有关么?

4. wait函数做了什么,如果只是休眠等待的话。producer会阻塞在lock函数上(consumer先lock了mutex)。