线程同步之条件变量

上一篇讲了互斥量是用来防止多个线程同时访问同一共享变量,本篇讲多线程同步常用的另一个机制:条件变量。

条件变量的作用,是允许一个线程将某个共享变量的状态变化通知其他线程。其他线程会等待这一通知,等待条件变量的线程将处于阻塞状态,直到信号到来,才会唤醒并执行后续操作。

与互斥量类似,条件变量也分为静态分配的条件变量和动态分配的条件变量。

对于静态分配的条件变量,在定义时对其进行初始化如下:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

对于动态分配的条件变量,或者静态分配的条件变量,但未使用默认属性对其初始化时,必须使用pthread_cond_init():

#include 
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);  //Return 0 on success, or a positive error number on error

参数cond为要进行初始化的条件变量,attr为属性对象,可由多个Pthreads函数对其进行初始化,若将attr置为NULL,则使用一组缺省属性来设置条件变量。与互斥量类似,当不再需要动态分配的条件变量时,需要使用pthread_cond_destroy()对其进行销毁:

#include 
int pthread_cond_destroy(pthread_cond_t *cond);

条件变量的主要操作是发送信号(signal)和等待(wait)。发送信号操作即通知一个或多个处于等待状态的线程,某个共享变量的状态已改变。等待操作是指在收到一个通知前,线程一直处于阻塞状态。常用函数定义如下:

#include 
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cont_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

发送信号有两种常用操作有pthread_cond_signal()和pthread_cond_broadcast(),两个函数均可针对由参数cond指定的条件变量而发送信号,不同之处在于,pthread_cond_signal()只保证唤醒至少一条遭到阻塞的线程,而pthread_cond_broadcast()则会唤醒所有被阻塞的线程。pthread_cond_wait()将阻塞某一线程,直到收到条件变量cond的通知。

pthread_cond_wait()的一个变体是pthread_cond_timedwait():

#include 
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);

pthread_cond_timedwait()通过一个参数abstime来指定一个线程等待条件变量通知时休眠时间的上限。如果abstime指定的时间间隔到期且无相关条件变量通知,则返回ETIMEOUT错误。

条件变量总会结合互斥量使用。条件变量对共享变量的状态改变发出通知,而互斥量则提供对该共享变量访问的保护。

pthread_cond_wait()在使用中,一般会置于while循环中。如下例,

pthread_mutex_lock(mutex);
while(/* Shared variable is not in the state we want */)
{
	pthread_cont_wait(&cond, *mutex);
}
pthread_mutex_unlock(&mutex);

共享变量的访问必须置于互斥量的保护下。共享变量和条件变量的使用步骤一般如下:
1. 线程在准备检查共享变量的状态时锁定互斥量;
2. 检查共享变量的状态;
3. 如果共享变量未满足预设条件,线程应在等待条件变量并进入休眠前解锁互斥量(以便其他线程能访问该共享变量);
4. 当线程被条件变量的通知而被再度唤醒时,必须对互斥量再次加锁,以便立即访问共享变量。
5. 对共享变量的访问完成后,若满足条件,则解锁,并执行线程后续操作。

看一个简单的实例,线程1负责对共享变量cnt累加,并在共享变量cnt累加超过5时,发送一个信号并退出;线程2在共享变量超过5时被唤醒:

#include 
#include 
#include 
#include 

static int cnt = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *func1(void *arg)
{
	for(int i = 0; i < 40000; i++)
	{
		pthread_mutex_lock(&mutex);
		cnt++;
		printf("Thread 1, cnt = %d \n", cnt);
		pthread_mutex_unlock(&mutex);
		
		sleep(1);
		
		if(cnt > 5)
		{
			pthread_cond_signal(&cond);
			printf("Thread 1 sent a signal. \n");
			break;
		}
	}
	
	return NULL;
}

void *func2(void *arg)
{
	pthread_mutex_lock(&mutex);
	while(cnt <= 5)
	{
		pthread_cond_wait(&cond, &mutex);
	}
	pthread_mutex_unlock(&mutex);
	
	printf("Thread 2 is waked up.\n");
	
	return NULL;
}

int main()
{
	pthread_t myThread_1, myThread_2;
	int ret;
	
	ret = pthread_create(&myThread_1, NULL, func1, NULL);
	if(ret != 0)
	{
		printf("Error create myThread_1! \n");
		exit(1);
	}
	
	ret = pthread_create(&myThread_2, NULL, func2, NULL);
	if(ret != 0)
	{
		printf("Error create myThread_2! \n");
		exit(1);
	}
	
	ret = pthread_join(myThread_1, NULL);
	if(ret != 0)
	{
		printf("Error join myThread_1! \n");
		exit(1);
	}
	ret = pthread_join(myThread_2, NULL);
	if(ret != 0)
	{
		printf("Error join myThread_2! \n");
		exit(1);
	}
	
	
	return 0;
}

执行结果如下:

线程同步之条件变量_第1张图片

你可能感兴趣的:(编程开发)