互斥锁和条件变量

互斥锁和条件变量

pthread_create创建线程
pthread_cancel终止另一个线程
pthread_detach设置线程以释放资源
pthread_join等待一个线程
pthread_exit退出线程
pthread_equal测试两个线程ID是否相等
pthread_self找出自己的线程ID

一、互斥量

互斥量是一种特殊的变量,它可以处于锁定状态(locked),也可以处于解锁状态(unlocked)状态。如果互斥量是锁定的,那么必然有一个线程持有或拥有这个互斥量。如果没有任何一个线程持有这个互斥量,那么这个互斥量就处于解锁、空闲或可用状态。当互斥量空闲,并且有一个线程试图获取这个互斥量时,这个线程就可以获得这个互斥量而不会被阻塞。如果互斥量处于锁定状态,那么试图获取这个互斥量的线程将被阻塞,并加入到这个互斥量的等待队列中。等待队列中的线程获得互斥量的顺序由实现系统决定。这样的机制解决了共享资源的互斥(Mutual Exclusive)访问问题。

1.创建并初始化一个互斥量
POSIX使用pthread_mutex_t类型的变量来表示互斥量。程序在使用pthread_ mutex_t变量之前,必须对其进行初始化。对于静态分配的pthread mutex_t变量,只要将PTHREAD_ MUTEX_ INITIALIZER赋给这个变量即可,对于动态创建或不使用默认属性的互斥量来说,就要调用pthread_ mutex_init函数来对其进行初始化。pthread mutex_init函数的形式为:

int pthread_mutex_init(pthread_mutex_t * restrict mutex, const pthread_mutexattr_t *restrict attr)

参数mutex是指向要初始化的互斥量的指针,参数attr是设定互斥量属性的变量的指针,如果为NULL,则使互斥量拥有默认属性。
如果成功,pthread mutex_init返回0,如果不成功,pthread_ mutex_init返回一个非零的错误码。下表列出了pthread_ mutex_init定义的错误码和错误原因:

 错误  			原因 
EAGAIN 	系统缺乏初始化*mutex所需的非内存资源
ENOMEM 	系统缺乏初始化*mutex所需的内存资源 
SPERM	调用程序没有适当的优先级

静态初始化通常比pthread_ mutex_init更有效,而且可以在定义为全局变量时即完成初始化,这样可以保证在任何线程开始执行之前,初始化即己完成。

2.销毁一个互斥量
当不再使用己经定义了的互斥量时,需要将互斥量销毁。函数pthread_mutex_destory用于销毁互斥量。它的形式为:

int pthread_mutex_destroy(pthread_ mutex_t *mutex);

参数mutex指向要销毁的互斥量。如果成功,函数返回0,如果不成功,函数返回一个非零的错误码。
可以用pthreadmutex_init重新初始化被销毁的互斥量。

3.对互斥量的锁定和解锁
POSIX中有两个可以用来获取互斥量的函数,pthread_mutex_lock和pthread_mutex_trylock。pthread_mutex_lock函数会使调用这个函数的线程一直阻塞到互斥量可用为止,而pthread_mutex_trylock会立即返回,如果互斥量空闲,那么调用这个函数的线程将获得互斥量,否则函数将返回EBUSY。 pthread_mutex unlock用于释放互斥量。
这三个函数的形式为:

int pthread_mutex_lock(pthread_mutex_ t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

如果成功,这些函数就返回0,如果不成功,这些函数就返回一个非零的错误码。

4.实例
由于在大多数机器中,对变量的增量和减量操作都不是原子的。比如通常情况下,增量操作包括三个步骤:将内存中的数值装载到CPU寄存器中将寄存器的值加1将寄存器中的值写回内存。而机器并不保证在这三步之间不会发生调度,这就导致对变量的增量和减量操作有可能得不到期望的结果。比如,当某线程正在进行增量操作,寄存器中的数值被加1之后发生了线程切换,另一个线程对同一个变量进行了增量操作后又切换回原线程,原线程将寄存器中的值写回内存。此时,变量只被增加了1,而不是增加了2。
要解决这个问题,就要保证对变量的增量和减量操作是原子的,也就是说,在操作过程中不允许发生线程切换。读者可以在程序中定义如下两个函数,使用它们来进行变量的增量和减量操作。

int increase(int *integer){
	static pthreaad_mutex_t PTHREAD_MUTEX_INITIALIZER;
	int error;
	if (error = pthread_mutex_lock(&lock);
	      return error;
	*integer++;
	return pthread_mutex_unlock(&lock);
}

int decrease(int *integer){
    static pthreaad_mutex_t lock = PTHREAD_UTEX_INITIALIZER;
    int error;
    if (error = pthread_mutex_lock(&lock);
            return error;
    *integer--;
    return pthread_mutex_unlock(&lock);
}

二、条件变量

条件变量是用来通知共享数据的状态信息的机制。由于涉及共享数据,因此条件变量是结合互斥量来使用的。

1.创建和销毁条件变量
POSIX用pthread_cond_t类型的变量来表示条件变量。程序必须在使用pthread_cond_t变量之前对其进行初始化。对那些静态分配的、使用默认属性的pthread_cond_t变量来说,可以直接将PTHREAD_ COND_INITIALIZER赋给变量就可以完成初始化。对那些动态分配的或不使用默认属性的变量来说,就要调用pthread_cond_init函数来执行初始化。该函数的形式为:

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

参数attr是一个条件变量属性对象,如果将NULL传递给attr,则初始化一个具有默认属性的条件变量,否则,就要用与线程属性对象类似的方式,先创建一个条件变量属性对象,再设置它。如果成功,pthread_cond_init返回0,如果不成功,pthread_cond_init就返回一个非零的错误码。下表列出了pthread_ cond_init的错误码和原因:

错误				原因
EAGAIN		系统缺乏初始化*mutex所需的非内存资源
ENOMEM		系统缺乏初始化*mutex所需的内存资源

函数pthread_cond_destroy销毁一个条件变量。该函数的形式为:

int pthread_cond_destroy(pthread_cond_t *cond)

如果成功,pthread_cond_destroy就返回0。如果不成功,它就返回一个非零的错误码。

2.等待和通知条件变量
条件变量是与断言或条件测试一同调用的,条件变量这个名称就是从这个事实中引申出来的。通常,线程会对一个断言进行测试,如果测试失败,就调用pthread_cond_wait。函数pthread_cond_timedwait可以用来等待一段有限的时间。这两个函数的第一个参数cond是一个指向条件变量的指针,第二个参数mutex是一个指向互斥量的指针。线程在调用等待条件变量的函数之前,应该拥有这个互斥量。当线程被放置在条件变量的等待队列中时,等待操作会释放这个互斥量。pthread_cond_timedwait函数的第三个参数是一个指向返回时间的指针,如果条件变量信号没有在此之前出现的话,线程就在这个时间返回。注意,这个值表示的是绝对时间,而不是时间间隔。这两个函数的形式为:

int pthread_cond_timedwait(pthread_coed_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);

如果成功,这两个函数返回0。如果不成功,它们返回一个非零的错误码。如果abstime指定的时间已经到期了,pthread_cond_timedwait就返回ETIMEDOUT。
当另一个线程修改了可能会使断言成真的变量时,它应该唤醒一个或多个在等待断言成真的线程。pthread_cond_signal函数只唤醒一个阻塞在cond指向的条件变量上的线程。pthread_cond_broadcast函数解除所有阻塞在cond指向的条件变量上的线程。这两个函数的形式为:

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

如果成功,这两个函数返回0,如果不成功,它们返回一个非零的错误码。等待条件变量的线程在被唤醒后会等待对应的互斥量,在它获得了互斥量之后,它才会被复原成就绪状态。

3.实例

#include <stdio.h>
#include <pthread.h>
int i=0;
pthread_mutex_t mutex = PTHREAD_UTEX_INITIALIZER;
pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;

void = threadfunc (void *pvoid){
	while (1){
		pthread_mutex_lock(&mutex);
		if (i<200){
			i++;
			pthread_cond_signal(&condvar);
			pthread_mutex_unlock(&mutex):
		}
		else{
			pthread_mutex_unlock(&mutex)
			break;
		}
	}
	return NULL;
}

int main(){
	pthread_t tid;
	pthread_create(&tid,  NULL,  &threadfunc,  NULL)
	pthread_mutex_lock(&mutex)
	while(i<100){
		pthread_cond_wait(&condvar, &mutex)
	}
	printf("i = %d\n",i);
	pthread_mutex_unlock(&mutex);
	pthread_join(tid,NULL);
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&condvar);
	return 0:
}

这是一个简单的条件变量应用程序。主线程监视全局变量1的值,如果1小于100,则等待。子线程递增1的值,直到1等于200。主线程直到1大于或等于100之后,才能继续执行。
运行该程序您会发现,程序的输出结果可能是“i=100",也可能是“i=xxx",其中xxx是一个100到200之间的数。这是因为子线程每次唤醒条件变量并释放互斥量之后,将与主线程一同竞争互斥量。当i大于100时,如果主线程获得互斥量,就会显示 i 的值。也就是说,等待条件变量的线程在被唤醒时,并不自动获得互斥量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值