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 的值。也就是说,等待条件变量的线程在被唤醒时,并不自动获得互斥量。