pthread几个互斥锁和条件变量接口



pthread_cleanup_push/pthread_cleanup_pop:

pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);  //可以注册pthread_mutex_unlock,也可以注册自定义的函数
pthread_mutex_lock(&mut);
//do something。此过程中假如本线程做某些事时异常退出,或者被别的线程cancel,那么mut就永远得不到执行
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);

pthread_cond_wait(&cond, &mut):

内部顺序执行pthread_mutex_unlock(&mut),pthread_cond_just_wait(&cond),pthread_mutex_lock(&mut),其中,前两个函数(unlock和just_wait)必须从语义上原子化。如果不原子化,会出现unlock之后别的线程获得mut,然后发送signal/broadcast,最后本线程调用just_wait。也就是说just_wait发生在signal/broadcast之前,所以本线程永远不会得到唤醒。

对于同一个cond,必须用同一个mut来配合使用。

使用示例如下:

pthread_mutex_lock(&mut);  
while(flag!=true)                //如果flag不为true,我们调cond_wait把自己给阻塞了
  pthread_cond_wait(&cond,&mut); //前后必须要加互斥锁,而pthread_cond_signal前后则无所谓加不加互斥锁
pthread_mutex_unlock(&mut);
pthread_cond_wait()前后必须加互斥锁,是为了保证对条件flag的互斥访问,如果不加互斥锁,可能出现如下情况:

thread1:                      thread2:
while(flag!=true)                                     //flag==false
                              flag=true;              //flag==true
pthread_cond_wait();                                  //thread1已进入阻塞状态,但其实此时flag是为true的,即漏过了这个条件了
..........
另外,pthread_cond_wait(&cond,&mut)内部大致包含下面几个步骤:

1、进入wait,处理条件变量,

2、解锁mut;

3、把自己给阻塞了;

4、睡眠;

5、睡眠;

6、。。。

7、收到别的线程通知(pthread_cond_signal/broadcast)可以醒了;

8、醒来;

9、尝试锁定mut,如果成功则函数返回;

10、不成功的话阻塞并等待mut可用。

以上步骤,可以由下图(来自pthread条件变量condition(配合mutex锁使用),经典,有图)描述:

pthread几个互斥锁和条件变量接口_第1张图片

此外,注意如果有多个线程在等待条件变量时,需要用while(flag!=true),在wait返回的时候再判断一次flag。原因是wait执行时可能存在下面的场景(signal/broadcast惊群,线程1和线程3同时收到信号):

线程1                                                           线程3
1、收到别的线程通知(pthread_cond_signal/broadcast)可以醒了;    1、收到别的线程通知(pthread_cond_signal/broadcast)可以醒了;
2、尝试加锁mut失败;                                            2、成功锁定mut;
3、睡眠                                                         3、置flag为false;
4、睡眠                                                         4、解锁mut;
5、成功锁定mut;
6、wait函数返回,线程醒来。

线程1醒来后必须再判断flag,flag可能为false了,因为线程1加锁mut失败的时候flag被别的线程修改了。


pthread_cond_signal():

有两种用法,都可以:

用法1:                                                                                                        用法2:

pthread_mutex_lock(&mut);                                                                     pthread_mutex_lock(&mut);

atomic_i++;                                                                                                  atomic_i++;

pthread_cond_signal(&cond);                                                                 pthread_mutex_unlock(&mut);

pthread_mutex_unlock(&mut);                                                                pthread_cond_signal(&cond);

两种用法各有优缺点:

用法1在某些OS的实现中,可能会造成signal之后还没unlock,但是另一个收到cond的线程在cond_wait()中最后试图lock,lock失败,导致重新陷入内核态,直到本线程unlock,由此带来性能损失。

用法2会出现unlock之后signal之前有别的低优先级线程抢占了mut,导致更高优先级的等待cond_wait的线程被低优先级线程抢占。

用法1在Linux下不会有上面提到的缺点,因为在Linux中,cond_signal只会让线程从cond_wait队列移到mutex_lock队列。

你可能感兴趣的:(pthread几个互斥锁和条件变量接口)