当前位置: 首页 > 编程笔记 >

浅谈互斥锁为什么还要和条件变量配合使用

雷献
2023-03-14
本文向大家介绍浅谈互斥锁为什么还要和条件变量配合使用,包括了浅谈互斥锁为什么还要和条件变量配合使用的使用技巧和注意事项,需要的朋友参考一下

mutex体现的是一种竞争,我离开了,通知你进来。

cond体现的是一种协作,我准备好了,通知你开始吧。

互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起配合使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。

两个线程操作同一临界区时,通过互斥锁保护,若A线程已经加锁,B线程再加锁时候会被阻塞,直到A释放锁,B再获得锁运行,进程B必须不停的主动获得锁、检查条件、释放锁、再获得锁、再检查、再释放,一直到满足运行的条件的时候才可以(而此过程中其他线程一直在等待该线程的结束),这种方式是比较消耗系统的资源的。而条件变量同样是阻塞,还需要通知才能唤醒,线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,该线程就休眠了,应该仍阻塞在这里,等待条件满足后被唤醒,节省了线程不断运行浪费的资源。这个过程一般用while语句实现。当线程B发现被锁定的变量不满足条件时会自动的释放锁并把自身置于等待状态,让出CPU的控制权给其它线程。其它线程 此时就有机会去进行操作,当修改完成后再通知那些由于条件不满足而陷入等待状态的线程。这是一种通知模型的同步方式,大大的节省了CPU的计算资源,减少了线程之间的竞争,而且提高了线程之间的系统工作的效率。这种同步方式就是条件变量。                                       

以上说明可能有点抽象,考虑这样的简单场景:通过伪代码说明。

A线程从队列中取元素,B线程往队列中存放元素。不考虑免锁的实现。需要一个mutex用来保护队列的一致性,避免两个线程同时操作队列破坏数据结构。

当队列为空的时候,A需要不断的探测队列状态 :

while(1)
{ 
if(队列为空)
休眠10s
else
    {
        加锁
        取元素
        解锁
     }
}

这就有一个问题,可能在刚进入休眠时,B放入元素了,但仍然需要休眠完整个10s的时间。造成不必要的延迟。当然如果不sleep,也可以,但会造成不必要的CPU开销。使用基于条件变量的事件通知唤醒机制,就可以避免这些问题。

一旦B放入元素完成后就执行pthread_cond_signal(),当前阻塞的线程就会立即被唤醒开始干活儿。

while(1) {
    pthread_mutex_lock();
    pthread_cond_wait();
    取元素;
    pthread_mutex_unlock();
}


条件变量都用互斥锁进行保护,条件变量状态的改变都应该先锁住互斥锁,pthread_cond_wait()需要传入一个已经加锁的互斥锁,该函数把调用线程加入等待条件的调用列表中,然后释放互斥锁,在条件满足从而离开pthread_cond_wait()时,mutex将被重新加锁,这两个函数是原子操作。

可以消除条件发生和线程睡眠等待条件发生间的时间间隙。其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量才能计算条件。

总而言之,为了避免因条件判断语句与其后的正文或wait语句之间的间隙而产生的漏判或误判,所以用一个mutex来保证: 对于某个cond的包括(判断,修改)在内的任何有关操作某一时刻只有一个线程在访问。也就是说条件变量本身就是一个竞争资源,这个资源的作用是对其后程序正文的执行权,于是用一个锁来保护。

这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会有任何变化。

感觉可以总结为:条件变量用于某个线程需要在某种条件成立时才去保护它将要操作的临界区,这种情况从而避免了线程不断轮询检查该条件是否成立而降低效率的情况,这是实现了效率提高。。。在条件满足时,自动退出阻塞,再加锁进行操作。

以上是关于效率问题,此外互斥锁还有一个缺点就是会造成死锁。

例如线程A和线程B都需要独占使用2个资源,但是他们都分别先占据了一个资源,然后又相互等待另外一个资源的释放,这样就形成了一个死锁。

条件变量起到了阻塞和唤醒线程的作用,所以通常互斥锁要和条件变量配合。

为了解决以上问题,条件变量常和互斥锁一起使用,条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。

以上就是小编为大家带来的浅谈互斥锁为什么还要和条件变量配合使用全部内容了,希望大家多多支持小牛知识库~

 类似资料:
  • 我试图在C语言中实现用餐哲学家的问题,使用pthon、互斥锁和条件变量。 它需要一个命令行参数来指定程序应该运行多长时间。我必须使用睡眠功能来完成这项任务 我的输出有一些问题: 使主函数在命令行上输入的秒数处于Hibernate状态似乎并没有使输出不同。 大多数哲学家都在渴望程序的大部分执行。 当我打印出一个哲学家在思考或吃饭时,一个“哲学家5”出现了,尽管应该只有0-4个哲学家。 这是我的密码:

  • Go语言包中的 sync 包提供了两种锁类型:sync.Mutex 和 sync.RWMutex。 Mutex 是最简单的一种锁类型,同时也比较暴力,当一个 goroutine 获得了 Mutex 后,其他 goroutine 就只能乖乖等到这个 goroutine 释放该 Mutex。 RWMutex 相对友好些,是经典的单写多读模型。在读锁占用的情况下,会阻止写,但不阻止读,也就是多个 gor

  • 本文向大家介绍浅谈Linux条件变量的使用,包括了浅谈Linux条件变量的使用的使用技巧和注意事项,需要的朋友参考一下 Linux线程同步之间存在多种机制,条件变量是一种类似操作系统里提到的生产者-消费者算法的同步机制,允许线程以无竞争的方式等待特定条件的发生。 示例伪代码: 条件变量需要配合互斥量一起使用,互斥量作为参数传入wait函数,函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,

  • 本文向大家介绍浅谈为什么要使用mybatis的@param,包括了浅谈为什么要使用mybatis的@param的使用技巧和注意事项,需要的朋友参考一下 起因 我们先来看一个报错 报错很简单,参数 start 没找到。 我是在实现一个 API 接口时发现了一个问题,当我不使用 @Param 标签时,mybatis 是不认识哪个参数叫什么名字的,尽管我定义了 (long start,long end)

  • Introduction This is the fourth part of the chapter which describes synchronization primitives in the Linux kernel and in the previous parts we finished to consider different types spinlocks and semap

  • 本文向大家介绍互斥锁死锁,包括了互斥锁死锁的使用技巧和注意事项,需要的朋友参考一下 死锁可以在使用互斥锁的多线程Pthread程序中发生。让我们看看它如何发生。未锁定的互斥锁由pthread_mutex_init()函数初始化。 使用pthread_mutex_lock()和pthread_mutex_unlock()获取并释放互斥锁。如果线程尝试获取锁定的互斥锁,则对pthread_mutex_