美文网首页
有了互斥锁,为什么还要条件变量?

有了互斥锁,为什么还要条件变量?

作者: AlanGuo | 来源:发表于2017-04-05 08:42 被阅读0次

    为了解决这个问题,首先要非常深入了解每一个概念:

    1. 互斥锁(mutual exclusive lock variable / mutex )

    互斥量(mutex)从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥锁加锁的线程将会阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为运行状态的线程可以对互斥锁加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。

    2. 条件变量

    条件变量(cond)是在多线程程序中用来实现"等待--》唤醒"逻辑常用的方法。条件变量利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使“条件成立”。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。线程在改变条件状态前必须首先锁住互斥量,函数pthread_cond_wait把自己放到等待条件的线程列表上,然后对互斥锁解锁(这两个操作是原子操作)。在函数返回时,互斥量再次被锁住。

    那为什么有互斥锁,还需要条件变量?

    因为:互斥锁和条件变量所解决的,是不同的问题,不同的场景。

    互斥锁解决的是在 shared memory space 模型下,多个线程对同一个全局变量的访问的竞争问题。由于写操作的非原子性(从内存中读进寄存器,修改,如果其他线程完成了对这个变量的修改,则旧的修改就被覆盖,等等问题),必须保证同一时间只有一个线程在进行写操作。这就涉及到了互斥锁,将临界区的操作锁起来,保证只有一个线程在进行操作。多个线程在等待同一把锁的时候,按照 FIFO 组织队列,当锁被释放时,队头线程获得锁(由操作系统管理,具体不表)。没有获得锁的线程继续被 block,换言之,它们是因为没有获得锁而被 block

    假如我们没有“条件变量”这个概念,如果一个线程要等待某个“自定义的条件”满足而继续执行,而这个条件只能由另一个线程来满足,比如 T1不断给一个全局变量 x +1, T2检测到x 大于100时,将x 置0,如果我们没有条件变量,则只通过互斥锁则可以有如下实现:

    /* 
     * Assume we have global variables:
     * int iCount == 0;
     * pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
     */
    
    //thread 1:
    
    while(true)
    {
        pthread_mutex_lock(&mutex);
        iCount++;
        pthread_mutex_unlock(&mutex);
    }
    
    
    //thread 2:
    while(true)
    {
        pthread_mutex_lock(&mutex);
        if(iCount >= 100)
        {
            iCount = 0;
        }
        pthread_mutex_unlock(&mutex);
    
    }
    

    这种实现下,就算 lock 空闲,thread2需要不断重复<加锁,判断,解锁>这个流程,会给系统带来不必要的开销。有没有一种办法让 thread2先被 block,等条件满足的时候再唤醒 thread2?这样 thread2 就不用不断进行重复的加解锁操作了?这就要用到条件变量了:

    
    //thread1 :
    while(true)
    {
        pthread_mutex_lock(&mutex);
        iCount++;
        pthread_mutex_unlock(&mutex);
    
        pthread_mutex_lock(&mutex);
        if(iCount >= 100)
        {
            pthread_cond_signal(&cond);
        }
        pthread_mutex_unlock(&mutex);
    }
    
    //thread2:
    while(1)
    {
        pthread_mutex_lock(&mutex);
        while(iCount < 100)
        {
            pthread_cond_wait(&cond, &mutex);
        }
        printf("iCount >= 100\r\n");
        iCount = 0;
        pthread_mutex_unlock(&mutex);
    }
    

    需要注意的是,条件变量需要配合互斥锁来使用:
    为什么要与pthread_mutex 一起使用呢? 这是为了应对 线程1在调用pthread_cond_wait()但线程1还没有进入wait cond的状态的时候,此时线程2调用了 cond_singal 的情况。 如果不用mutex锁的话,这个cond_singal就丢失了。加了锁的情况是,线程2必须等到 mutex 被释放(也就是 pthread_cod_wait() 释放锁并进入wait_cond状态 ,此时线程2上锁) 的时候才能调用cond_singal.

    简而言之就是,在thread 1 call pthread_cond_wait() 的时刻到 thread 1真正进入 wait 状态时,是存在着时间差的。如果在这段时间差内 thread2 调用了 pthread_cond_signal() 那这个 signal 信号就丢失了。给 wait 加锁可以防止同时有另一个线程在 signal。

    (好像还有其他原因,有空补。)

    相关文章

      网友评论

          本文标题:有了互斥锁,为什么还要条件变量?

          本文链接:https://www.haomeiwen.com/subject/swhtattx.html