在多线程编程中仅使用互斥锁来完成互斥是不够用的, 如如下情形:
假设有两个线程 t1 和 t2, 须要这个两个线程循环对一个共享变量 sum 进行自增操做,那么 t1 和 t2 只须要使用互斥量便可保证操做正确完成,线程执行代码如所示:
编程
pthread_mutex_t sumlock= PTHREAD_MUTEX_INITIALIZER;
void * t1t2(void) { pthread_mutex_lock(&sumlock); sum++; pthread_mutex_unlock(&sumlock); }
若是这时须要增长另外一个线程 t3,须要 t3 在 count 大于 100 时将 count 值从新置 0 值,那么能够 t3 能够实现以下:
多线程
void * t3 (void) { pthread_mutex_lock(&sumlock); if (sum >= 100) { sum = 0; pthread_mutex_unlock(&sumlock); } else { pthread_mutex_unlock(&sumlock); usleep(100); } }
以上代码存在如下问题:函数
1) sum 在大多数状况下小于 100, 那么对 t3 的代码来讲,大多数状况下, 走的是 else 分支, 只是 lock 和 unlock,而后 sleep()。 这浪费了 CPU 处理时间。测试
2) 为了节省 CPU 处理时间, t3 会在探测到 sum 没到达 100 的时候 usleep()一段时间。这样却又带来另一个问题, 亦即 t3 响应速度降低。 可能在 sum 到达 200 的时候, t3 才会醒过来。 spa
这样时间与效率出现了矛盾,而条件变量就是解决这个问题的好方法。 线程
Pthreads 用 pthread_cond_t 类型的变量来表示条件变量。程序必须在使用 pthread_cond_t变量以前对其进行初始化。指针
对于静态分配的变量能够简单地将 PTHREAD_COND_INITIALIZER 赋值给变量来初始化默认行为的条件变量。rest
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
对动态分配或者不使用默认属性的条件变量来讲可使用 pthread _cond_init()来初始化。函数原型以下:
code
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参数 cond 是一个指向须要初始化 pthread_cond_t 变量的指针,参数 attr 传递 NULL 值时, pthread_cond_init()将 cond 初始化为默认属性的条件变量。
blog
函数成功将返回 0;不然返回一个非 0 的错误码。
静态初始化程序一般比调用 pthread_cond_init()更有效,并且在任何线程开始执行以前,确保变量被执行一次。
如下代码示例了条件变量的初始化。
pthread_cond_t cond; int error; if (error = pthread_cond_init(&cond, NULL)); fprintf(stderr, "Failed to initialize cond : %s\n", strerror(error));
函数 pthread_cond_destroy()用来销毁它参数所指出的条件变量,函数原型以下:
int pthread_cond_destroy(pthread_cond_t *cond);
函数成功调用返回 0,不然返回一个非 0 的错误码。如下代码演示了如何销毁一个条件变量。
pthread_cond_t cond; int error; if (error = pthread_cond_destroy(&cond)) fprintf(stderr, "Failed to destroy cond : %s\n", strerror(error));
条件变量是与条件测试一块儿使用的,一般线程会对一个条件进行测试,若是条件不知足就会调用条件等待函数来等待条件知足。
条件等待函数有 pthread_cond_wait()pthread_cond_timedwait()和两个,函数原型以下:
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
pthread_cond_wait()函数在条件不知足时将一直等待, 而 pthread_cond_timedwait()将只等待一段时间。
参数 cond 是一个指向条件变量的指针,参数 mutex 是一个指向互斥量的指针,线程在调用前应该拥有这个互斥量,当线程要加入条件变量的等待队列时,等待操做会使线程释放这个互斥量。 pthread_timedwait()的第三个参数 abstime 是一个指向返回时间的指针,若是条件变量通知信号没有在此等待时间
以前出现,等待将超时退出, abstime 是个绝对时间,而不是时间间隔。
以上函数成功调用返回 0,不然返回非 0 的错误码,其中 pthread_cond_timedwait() 函数若是 abstime 指定的时间到期,错误码为 ETIMEOUT。
如下代码使得线程进入等待,直到收到通知而且知足 a 大于等于 b 的条件。
pthread_mutex_lock(&mutex) while(a < b) pthread_cond_wait(&cond, &mutex) pthread_mutex_unlock(&mutex)
当另外一个线程修改了某参数可能使得条件变量所关联的条件变成真时,它应该通知一个或者多个等待在条件变量等待队列中的线程。
条件通知函数有 pthread_cond_signal()和 pthread_cond_broadcast()函数,其中 pthread_cond_signal 函数能够唤醒一个在条件变量等待队列等待的线程,而 pthread_cond_broadcast函数能够全部在条件变量等待队列等待的线程。函数原型以下:
int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond);
参数 cond 是一个指向条件变量的指针。函数成功返回 0,不然返回一个非 0 的错误码。
#include<stdio.h> #include<string.h> #include<pthread.h> #include<stdlib.h> #include<unistd.h> pthread_t tid[3]; int sum = 0; pthread_mutex_t sumlock = PTHREAD_MUTEX_INITIALIZER; /* 静态初始化互斥量 */ pthread_cond_t cond_sum_ready = PTHREAD_COND_INITIALIZER; /* 静态初始化条件变量 */
void * t1t2(void *arg) { int i; long id = (long)arg; for (i = 0; i < 60; i++) { pthread_mutex_lock(&sumlock); /* 使用互斥量保护临界变量 */ sum++; printf("t%ld: read sum value = %d\n", id + 1 , sum); pthread_mutex_unlock(&sumlock); if (sum >= 100) pthread_cond_signal(&cond_sum_ready); /* 发送条件通知,唤醒等待线程 */ } return NULL; }
void * t3(void *arg) { pthread_mutex_lock(&sumlock); while(sum < 100) /* 不知足条件将一直等待 */ pthread_cond_wait(&cond_sum_ready, &sumlock); /* 等待条件知足 */ sum = 0; printf("t3: clear sum value\n"); pthread_mutex_unlock(&sumlock); return NULL; }
int main(void) { int err; long i; for (i = 0; i < 2; i++) { err = pthread_create(&(tid[i]), NULL, &t1t2, (void *)i); /* 建立线程 1 线程 2 */ if (err != 0) { printf("Can't create thread :[%s]", strerror(err)); } } err = pthread_create(&(tid[2]), NULL, &t3, NULL); /* 建立线程 3 */ if (err != 0) printf("Can't create thread :[%s]", strerror(err)); for (i = 0; i < 3; i++) pthread_join(tid[i], NULL); return 0; }
运行结果以下所示, sum 累加到 100 时发送条件通知,但程序结果中 sum 计算到 103 时, t3 才被调用,这是由于 signal 与 wait 调用之间有间隙存在。
t1: read sum value = 1 t1: read sum value = 2 ... t2: read sum value = 100 t1: read sum value = 101 t1: read sum value = 102 t1: read sum value = 103 t3: clear sum value t2: read sum value = 1 ....... t2: read sum value = 17