互斥量和条件变量

ps:参考了不少博客,可是当时没记下连接。。。linux

互斥器和条件变量用法以下:

pthread_mutex_lock(&lock);
while (condition_is_false) {
    pthread_cond_wait(&cond, &lock);
}

上面那个while能换成if吗?答案是不能,不然会致使spurious wakeup虚假唤醒。由于不只要在pthread_cond_wait前要检查条件是否成立,在pthread_cond_wait以后也要检查。由于pthread_cond_wait不只能被pthread_cond_signal/pthread_cond_broadcast唤醒,并且还会被其它信号唤醒,后者就是虚假唤醒。
并且有可能多个线程被同时唤醒。那么在第一个获取完资源后,后面的全都没法获取资源了。
pthread_cond_wait内的互斥量只能保证同步。性能

linux的pthread_cond_wait是用futex系统调用,这个是慢速系统调用,看过apue知道任何慢速系统调用被信号打断的时候会返回-1,而且把errno置为EINTR,若是慢速系统调用的重启功能被关闭,须要在调用该系统调用的地方手动重启它,像下面这样:线程

while (1) {
    int ret = syscall();
    if (ret < 0 && errno == EINTR)
        continue;
    else
        break;
}

可是futex不能这么用,由于futex结束后到再次重启这个过程有个时间窗,在这个窗口内可能发生了pthread_cond_signal/phread_cond_broadcast,若是发生这种状况,再进行pthread_cond_wait的时候就错过了一次条件变量的变化,就会无限等待下去。可是若是不像上面那样写又没法重启futex系统调用,咋整呢?这就回到了上面检查布尔条件的时候为何用while而不用if。code

用while不会由于虚假唤醒而错过phread_cond_signal/pthread_cond_broadcast,并且在经过判断while条件不成立检测出这次唤醒为虚假唤醒并继续调用futex继续等待。队列


为何要有条件变量:

举个例子:在应用程序中有连个线程thread1,thread2,thread3和thread4,有一个int类型的全局变量iCount。iCount初始化为0,thread1和thread2的功能是对iCount的加1,thread3的功能是对iCount的值减1,而thread4的功能是当iCount的值大于等于100时,打印提示信息并重置iCount=0。
hread4并不知道何时iCount会大于等于100,因此就会一直在循环判断,可是每次判断都要加锁、解锁(即便本次并无修改iCount)。这就带来了问题一,CPU浪费严重。
因此经过条件变量能够避免CPU的浪费,并及时通知。资源


释放顺序:

  • (1) 按照 unlock(mutex); condition_signal()顺序, 当等待的线程被唤醒时,由于mutex已经解锁,所以被唤醒的线程很容易就锁住了mutex而后从conditon_wait()中返回了。
  • (2) 按照 condition_signal(); unlock(mutext)顺序,当等待线程被唤醒时,它试图锁住mutex,可是若是此时mutex还未解锁,则线程又进入睡眠,mutex成功解锁后,此线程在再次被唤醒并锁住mutex,从而从condition_wait()中返回。

能够看到,按照(2)的顺序,对等待线程可能会发生2次的上下文切换,严重影响性能。所以在后来的实现中,对(2)的状况,若是线程被唤醒可是不能锁住mutex,则线程被转移(morphing)到互斥量mutex的等待队列中,避免了上下文的切换形成的开销。 -- wait morphing同步

相关文章
相关标签/搜索