线程间同步机制----条件变量

一、条件变量基本原理


条件变量是线程可用的另一种同步机制。条件变量给多个线程提供了一个回合的场所。条件变量与互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生。条件本身是由互斥量保护的。线程在改变条件状态之前必须要首先锁住互斥量。其他线程在获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定以后才能计算条件。


二、条件变量基本操作


1、初始化、销毁条件变量


使用条件变量之前,需要定义该条件变量(全局变量),定义条件变量对象如下:

<span style="font-family:Microsoft YaHei;font-size:18px;">pthread_cond_t   condtion;</span>


初始化:由pthread_cond_t数据类型表示的条件变量可以用两种方式进行初始化,可以把常量PTREAD_COND_INITIALIZER赋给静态分配的条件变量,但如果条件变量是动态分配的,则需要使用pthread_cond_init函数对它经行初始化。

<span style="font-family:Microsoft YaHei;font-size:18px;">#include  <pthread.h>
int  pthread_cond_init(pthread_cond_t  *restrict  cond,  const pthread_condattr_t  *restrict attr);
//若成功,返回0;否则,返回错误编号</span>

第一个参数cond是指向要初始化的条件变量的指针。

第二个参数cond_attr是指向属性对象的指针,该属性对象定义要初始化的条件变量的特性,如果该指针为NULL,则使用默认的属性。



销毁:

<span style="font-family:Microsoft YaHei;font-size:18px;">#include  <pthread.h>
int  pthread_cond_destory(pthread_cond_t  *cond);
//若成功,返回0;若失败,返回错误编号</span>

第一个参数cond是指向要销毁的条件变量的指针。


2、通知等待条件变量的线程


<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h>
int  pthread_cond_signal(pthread_cond_t  *cond);
int  pthread_cond_broadcast(pthread_cond_t  *cond);
//若成功,返回0;若失败,返回错误编号。</span>

其参数cond是指向要通知或广播的条件变量的指针。


(1)pthread_cond_signal()函数用来唤醒等待出现与条件变量cond关联的条件的第一个线程。如果cond上没有阻塞任何线程,则函数不起作用。如果cond阻塞了多个线程,则调度策略将确定要取消阻塞的线程。显然,在此函数中,隐含的释放了当前线程占有的信号量。

(2)pthread_cond_broadcast()函数用来唤醒等待出现与条件变量cond关联的所有线程。如果cond上没有阻塞任何线程,则此函数不起作用。


3、等待条件变量


(1)pthread_cond_wait()函数用来阻塞等待某个条件变量。


<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h>
int  pthread_cond_wait(pthread_cond_t   *restrict  cond, pthread_mutex_t  *restrict  mutex);//若成功,返回0;若失败,返回错误编号。</span>


第一个参数cond是指向要等待的条件变量的指针。

第二个参数mutex是指向与条件变量cond关联的互斥锁的指针。

(2)pthread_cond_timedwait()函数将在指定的时间范围内等待条件变量。


<span style="font-family:Microsoft YaHei;font-size:18px;">#include <pthread.h>
int  pthread_cond_timedwait(pthread_cond_t  *restrict  cond, pthread_mutex_t   *restrict  mutex,  const  struct  timespec  *restrict  tsptr);
//若成功,返回0;否则,返回错误编号。</span>

第一个参数cond是指向要等待的条件变量的指针。

第二个参数mutex是指向与条件变量cond关联的互斥锁的指针。

第三个参数abstime是等待过期时间的绝对时间,如果在此时间范围内取到该条件变量函数将返回。该时间为从1970-1-1:0:0:0以来的秒数,即为一个绝对时间。该数据结构声明如下:

<span style="font-family:Microsoft YaHei;font-size:18px;">struct  timespec {
        long  ts_sec;
        long  ts_nsec;
};</span>


以上两个函数都包含一个互斥锁,如果某线程因等待条件变量进入等待状态时,将隐含释放其申请的互斥锁,同样,在返回时,首先要申请到该互斥锁对象。


三、条件变量应用实例


1、功能说明


此程序用来处理生产消费问题,整个临时存储空间为2,即在任意时刻,最多能够有2个产品存放在临时空间,如果已经有2个产品存放在临时空间,将阻塞生产线程。同理,如果临时空间没有产品,显示需要阻塞消费线程。此程序中主要实现了生产和消费两个线程的同步。


在此程序中,使用了互斥锁对象以及条件变量两种线程间通信机制。互斥锁与条件变量协同工作,操作步骤如下:

(1)锁定互斥锁。

(2)测试条件是否满足。

(3)如果满足,执行操作,完成后解锁互斥锁。

(4)如果第2步的条件不满足,使用条件变量机制等待,当另一个线程使此条件满足时,执行第3步。


2、源代码分析


<span style="font-family:Microsoft YaHei;font-size:18px;">#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define BUFFER_SIZE 2	//时间空间大小

struct prodcons			//条件信息结构体
{
	int buffer[BUFFER_SIZE];	//生产产品值
	pthread_mutex_t lock;		//互斥锁
	int readpos, writepos;		//读写位置
	pthread_cond_t notempty;	//条件变量,表非空
	pthread_cond_t notfull;		//条件变量,表非满
};

void init(struct prodcons *prod)	//初始化
{
	pthread_mutex_init(&prod->lock, NULL);//初始化互斥锁
	pthread_cond_init(&prod->notempty, NULL);//初始化条件变量
	pthread_cond_init(&prod->notfull, NULL);//初始化条件变量
	prod->readpos = 0;		//初始化读操作位置
	prod->writepos = 0;		//初始化写操作位置
}

void put(struct prodcons *prod, int data)	//输入产品子函数
{
	pthread_mutex_lock(&prod->lock);	//锁定互斥量
	while((prod->writepos + 1)%BUFFER_SIZE == prod->readpos)
	{						//测试空间是否已满
		printf("producer wait for not full\n");
		pthread_cond_wait(&prod->notfull, &prod->lock);//等待有空间可写
	}
	prod->buffer[prod->writepos] = data;	//写数据
	prod->writepos++;						//写位置数加1
	if(prod->writepos >= BUFFER_SIZE)		//如果写到尾部,返回
		prod->writepos = 0;
	pthread_cond_signal(&prod->notempty);	//发送有数据信号
	pthread_mutex_unlock(&prod->lock);		//解锁
}
int get(struct prodcons *prod)
{
	int data;
	pthread_mutex_lock(&prod->lock);	//锁上互斥锁
	while(prod->writepos == prod->readpos)
	{									//测试是否有数据
		printf("consumer wait for not empty\n");
		pthread_cond_wait(&prod->notempty, &prod->lock);
	}						//如果为空,等待
	data = prod->buffer[prod->readpos];	//读数据
	prod->readpos++;					//读指针加1
	if(prod->readpos >= BUFFER_SIZE)	//如果读到尾部,返回
		prod->readpos = 0;
	pthread_cond_signal(&prod->notfull);//发有空间空闲信号
	pthread_mutex_unlock(&prod->lock);	//解锁
	return data;
}

#define OVER (-1)

struct prodcons buffer;

void * producer(void *data)	//生产者
{
	int n;
	for(n = 1; n <= 5; n++)	//生产前5个产品
	{
		printf("producer sleep 1 second .....\n");
		sleep(1);		//每1秒生产一个产品
		printf("put the %d producer \n", n);
		put(&buffer, n);
	}
	for(n = 6; n <= 10; n++)	//生产后5个产品
	{
		printf("producer sleep 3 second ......\n");
		sleep(3);		//每3秒生产一个产品
		printf("put the %d product \n", n);
		put(&buffer, n);
	}
	put(&buffer, OVER);
	printf("producer stopped!\n");
	return NULL;
}

void * consumer(void *data)	//消费者
{
	int d=0;
	while(1)
	{
		printf("consumer sleep 2 second ......\n");
		sleep(2);		//每2秒消费一个产品
		d = get(&buffer);
		printf("get the %d product\n", d);
		if(d == OVER)
			break;
	}
	printf("consumer stopped!\n");
	return NULL;
}

int main(int argc, char *argv[])
{
	pthread_t th_a, th_b;
	void * retval;
	init(&buffer);
	pthread_create(&th_a, NULL, producer, 0);//创建生产线程
	pthread_create(&th_b, NULL, consumer, 0);//创建消费线程
	pthread_join(th_a, &retval);	//等待生产线程结束
	pthread_join(th_b, &retval);	//等待消费线程结束
	return 0;
}
</span>

注:实现算法上有问题,重点放在线程的条件变量和互斥量的使用上。


你可能感兴趣的:(多线程,条件变量)