二 Linux--多线程( 四 )


两个进程都想获得对方的锁,造成死锁 。
条件变量概念利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使“条件成立”(给出条件成立信号) 。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起 。
同步: 在保证数据安全的前提下 , 让线程能够按照某种特定的顺序访问临界资源 , 从而避免饥饿问题,叫做同步
为什么存在线程同步?
线程同步使得每个线程都能够访问临界资源,多个线程协同高效完成某些任务 。
条件变量如何与互斥锁结合使用?
条件变量是包含一个等待队列的 。多个线程可以去竞争一把锁 , 没有得到锁资源的线程会在锁上继续挂起等待,当拥有锁的线程条件变量满足时 , 会先释放锁资源,然后进入到条件变量的等待队列去等待(等待其他线程唤醒),这样其他线程就可以获得锁资源,如果此时唤醒的条件变量满足,该线程可以去唤醒等待队列中的第一个线程,自己释放锁资源 , 然后让第一个线程重新拥有锁资源,依次如此 , 多个线程就是顺序地执行工作 。这样就可以实现线程同步的操作 。
与互斥锁不同的是,条件变量是用来等待而不是用来上锁的,条件变量本身就不是锁!
条件变量用来自动阻塞一个线程,直到某种特殊情况发生为止,通常和互斥锁一起使用 。
条件变量的两个动作:

  • 条件不满 , 阻塞线程
  • 条件满足,通知阻塞的线程开始工作
条件变量的类型:pthread_cond_t
条件变量的接口条件变量是一个类型为pthread_cond_t的条件变量,课通过定义变量的方式来定义一个条件变量
  • 条件变量初始化
int pthread_cond_init(pthread_cond_t *restrict cond,  const pthread_condattr_t *restrict attr);功能: 初始化一个条件变量参数: cond:指向要初始化的条件变量指针 attr:条件变量属性,通常为默认值 , 传入NULL即可  也可以使用静态初始化的方法 , 初始化条件变量:pthread_cond_t cond = PTHREAD_COND_INITIALIZER返回值: 成功:0 失败:非0错误号
  • 条件变量的销毁
int pthread_cond_destroy(pthread_cond_t *cond);功能: 销毁一个条件变量参数: cond:指向要始化的条件变量指针返回值: 成功:0 失败:非0错误号
  • 等待条件变量满足
int pthread_cond_wait(pthread_cond_t *restrict  cond,pthread_mutex_t *restrict mutex);功能: 阻塞等待一个条件变量 a)阻塞等待条件变量cond(参1)满足 b)释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);a)b)两步为一个原子操作 c)当被唤醒 , pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);参数: cond:指向要初始化的条件变量指针 mutex:互斥锁返回值: 成功:0 失败:非0错误号为什么pthread_cond_wait需要互斥量?
条件变量是实现线程同步的一种手段,如果一个线程进入等待队列还不释放锁资源,这样其他线程也不能够得到锁资源,这样唤醒线程的条件变量永远不可能满足,那么这个线程也将一直等待下去 。所以一个线程进入等待队列需要释放自己手中的锁资源来实现真正地同步
  • 唤醒条件变量
int pthread_cond_signal(pthread_cond_t *cond)功能: 唤醒阻塞队列上的第一个线程参数: cond指向要初始化的条件变量指针返回值: 成功:0 失败:非0错误号int pthread_cond_broadcast(pthread_cond_t *cond)功能: 唤醒全部阻塞在条件变量上的线程参数: cond:指向要初始化的条件变量指针返回值: 成功:0 失败:非0错误号后者是唤醒等待队列中所有的线程,而前者只唤醒等待队列中的第一个线程 。后者会带来一个很不好的效应——惊群效应 。多个线程同时被唤醒,但是最终只有一个线程能够获得“控制权”,其他获得控制权失败的线程可能重新进入休眠状态 。等待获得控制权的线程释放锁资源后去通知下一个线程,这样就容易引起OS和CPU的管理调度负担,所以不建议使用 。实例演示: 创建五个线程 , 四个线程执行run1,上来就在条件变量下等待,另一个线程执行run2,然后无脑唤醒等待队列下的线程

推荐阅读