注:摘自肖堃老师视频教程
任务同步- 任务间相互合作关系(直接相互制约关系)
两个或多个任务为了合作完成同一个工作,在执行速度或某个确定的时序点上必须相互协调,即一个任务的执行必须依赖另一个任务的执行情况。
程序设计中存在这样的情况 :多个线程都要访问临界资源,又要相互合作(线程间同时存在互斥关系和同步关系)
例如:线程A先执行某操作(例如对全局变量x的修改)后,线程B才能(根据变量X的值判断)执行另一个操作(可能是对变量x的修改),该如何实现?
linux提供了条件变量机制:条件变量与互斥量一起使用时,允许线程以互斥的方式阻塞等待特定条件的发生(同步)
步骤:
1)定义条件变量(pthread_cond_t类型),定义互斥量变量
2)初始化条件变量,初始化互斥量
3)触发条件线程x
互斥量加锁 -> xx操作 -> 触发条件变量 -> 互斥量解锁
4)等待条件线程y
互斥量加锁 -> 等待条件变量 -> xx操作 -> 互斥量解锁
5)销毁条件变量,销毁互斥量变量
1)条件变量定义和初始化
静态初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态初始化:
#include <pthread.h>
int pthread_cond_init( pthread_cond_t *cond, pthread_condattr_t *attr );
参数和返回值:
cond - 条件变量
attr - 条件变量属性,若为NULL,则使用默认属性
成功返回0,出错返回错误码
2)条件变量销毁
int pthread_cond_destroy( pthread_cond_t *cond )
参数和返回值:
cond - 条件变量
成功返回0,出错返回错误码
3)条件变量使用
pthread_cond_wait函数将使调用线程进入阻塞状态,直到条件被触发
int pthread_cond_wait( pthread_cond_t *cond, pthread_mutex_t *mutex)
参数和返回值:
cond - 条件变量
mutex - 互斥量
成功返回0,失败返回错误码(可能传入的条件变量指针不正确,也可能传入的互斥量指针不正确)
为什么条件变量需要和互斥量配合使用
1> 条件变量的使用场景伴随共享资源的使用,例如全局变量
2> 在调用pthread_cond_wait前,需要使互斥量处于加锁状态,这样可以通过原子操作的方式,将调用线程放到该条件变量等待线程队列(临界资源)中
等待条件变量的操作:
1> 调用pthread_mutex_lock()
2> 调用pthread_cond_wait()
3> 调用pthread_mutex_unlock()
由于pthread_cond_wait()在被执行之前,需要先调用pthread_mutex_lock()进行互斥量加锁, 而pthread_cond_wait()可能导致调用该函数的线程被阻塞,
那么是否意味着被阻塞的线程保持了互斥量的加锁操作,从而导致系统的死锁呢?
其实,linux系统已经考虑到了这种情况,当调用pthread_cond_wait()后,内核会自动执行操作:
a. 在线程阻塞等待条件变量之前,调用pthread_mutex_unlock
b. 若条件变量被其他线程触发,在该线程被唤醒后,调用pthread_mutex_lock,再次将该互斥量加锁
以上操作由内核自动完成,因此,调用pthread_cond_wait函数,无需考虑因为对互斥量加锁并阻塞,而导致可能出现的死锁的情况。
pthread_cond_signal和pthread_cond_broadcast可以触发条件变量并唤醒等待条件变量的线程
pthread_cond_signal唤醒该条件变量等待线程队列中的某一个线程
pthread_cond_broadcast唤醒该条件变量等待队列中的所有线程,这些线程会进行竞争
pthread_cond_signal( pthread_cond_t *cond )
pthread_cond_broadcast( pthread_cond_t *cond )
输入参数和返回值:
cond - 条件变量
成功返回0,失败返回错误码
#include <stdio.h> #include <pthread.h> pthread_mutex_t count_lock; pthread_cond_t count_ready; int count = 0; void *decrement_count( void *arg ) { pthread_mutex_lock( &count_lock ); printf( "decrement:waiting...... " ); //进入阻塞状态,内核会先mutex_unlock,直到条件变量被唤醒,内核在执行mutex_lock pthread_cond_wait( &count_ready, &count_lock ); //等待条件变量,期间互斥量仍然可用 count = count - 1; printf( "decrement: count = %d ", count ); pthread_mutex_unlock( &count_lock ); printf( "decrement: quit " ); pthread_exit( NULL ); } void *increment_count( void *arg ) { pthread_mutex_lock( &count_lock ); printf( "increment:running " ); count += 1; pthread_cond_signal( &count_ready ); //触发条件变量 printf( "increment: count = %d ", count ); pthread_mutex_unlock( &count_lock ); printf( "increment: quit " ); pthread_exit( NULL ); } int main( ) { pthread_t tid1, tid2; count = 0; pthread_mutex_init( &count_lock, NULL ); pthread_cond_init( &count_ready, NULL ); pthread_create( &tid1, NULL, decrement_count, NULL ); //创建减法线程 sleep(2); pthread_create( &tid2, NULL, increment_count, NULL ); //创建加法线程 pthread_join( tid2, NULL ); printf( "decrement quit " ); pthread_join( tid1, NULL ); return 0; }
编译并运行:
root@localhost:pthread# gcc -o thread_mutex_cond thread_mutex_cond.c -lpthread
root@localhost:pthread# ./thread_mutex_cond
decrement:waiting......
increment:running
increment: count = 1
increment: quit
decrement: count = 0
decrement: quit
decrement quit