当前位置:首页 > 技术知识 > 正文内容

Linux系统编程:条件变量为什么要用锁

maynowei10个月前 (08-03)技术知识154

条件变量可以解决线程同步和共享资源访问的问题,条件变量是对互斥锁的补充,它允许一个线程阻塞并等待另一个线程发送的信号,当收到信号时,阻塞的线程被唤醒并试图锁定与之相关的互斥锁。具体定义如下:

等待:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abtime);

唤醒:

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

具体使用场景:

在等待线程实现如下:

pthread_mutex_lock(&same_mutex);

pthread_cond_wait(&m_cond, &same_mutex);

pthread_mutex_unlock(&same_mutex);

在唤醒线程实现如下:

pthread_mutex_lock(&same_mutex);

pthread_cond_signal(&m_cond);

pthread_mutex_unlock(&same_mutex);

等待线程会在pthread_cond_wait()调用时挂起,并且在锁的保护下构造和检查好m_cond对应的条件链表后先解锁same_mutex,然后睡眠。这里解锁和睡眠是一个原子操作。当等待线程收到唤醒线程的通知后,pthread_cond_wait做的第一件事是把same_mutex重新加锁并唤醒返回,这里加锁和唤醒也是一个原子操作。

那么唤醒线程和等待线程为什么要用同一个mutex锁呢?首先,如果在等待线程调用了pthread_cond_wait()但是还没有开始等待(因为之前还要做链表的初始化工作),这时唤醒操作如果发生则会导致唤醒丢失(竞态条件)。因此必须使用锁防止条件链表这个共享资源被同时操作。(等待线程先在锁保护下操作完共享链表,然后解锁和睡眠是原子操作后开始睡眠,因此唤醒操作如果是在同一把锁保护中才能唤醒的话(唤醒也会修改共享链表),等待线程一定已经访问完共享资源并且睡眠了,就防止了冲突丢失唤醒信号)。 但是如果当前没有线程阻塞等待唤醒,pthread_cond_signal()函数调用后应没有任何效果。

因此条件变量使用锁是为了防止唤醒和等待操作同时发生的竞态条件,从而导致唤醒丢失甚至引起死锁。但是如果是认为不等待直接唤醒则该唤醒会直接丢失。

注:

最后pthread_cond_wait唤醒和加锁也是一个原子操作,如果有多个线程在同一个条件变量上等待,可以保证虽然都被唤醒了但是只有一个线程会获取到锁。

相关文章

Android监听滚动视图(监听页面滚动)

Android UI Libs之Android-ObservableScrollView1. 说明Android-ObservableScrollView,顾名思义,Android上观察滚动的视图,可...

Android主流UI开源库整理(android完整开源项目)

前言最近老大让我整理一份 Android主流UI开源库 的资料,以补充公司的Android知识库。由于对格式不做特别限制,于是打算用博客的形式记录下来,方便查看、防丢并且可以持续维护、不断更新。标题隐...

C# 中的多线程同步机制:lock、Monitor 和 Mutex 用法详解

在多线程编程中,线程同步是确保多个线程安全地访问共享资源的关键技术。C# 提供了几种常用的同步机制,其中 lock、Monitor 和 Mutex 是最常用的同步工具。本文将全面介绍这三种同步机制的用...

Linux C++实现多线程同步的四种方式(超级详细)

背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题?通过多线程模拟多窗口售票为例:#include <iostream>#include<pthread.h>#inc...

如何在Go中同步线程(go语言同步锁)

单线程代码已经带来头痛。添加第二个线程,就是从基础头痛升级了。解决方案?互斥锁:线程和数据的交通警察。一旦你理解了它们,线程同步就变成了第二本能,语言无关。在C++和Go中工作,我遇到过所有常见的混乱...