pthread_cond_timedwait的使用及疑问



条件变量是一种在并发编程中常用的同步原语。是一种通知机制,一个线程需要某种条件成立后,才能继续执行,如果条件不成立则阻塞等待条件成立,是wait端;另外的线程则是执行某些操作后,使条件成立,然后唤醒等待线程,是signal/broadcast端。

wait端的使用方式:

  1. 由于条件会被wait线程读取,被signal/broadcast线程修改,即写入。为了防止出现竞争,需要和mutex一起使用,使用mutex来保护条件。
  2. 在mutex已经锁住的情况下,才能调用wait。
  3. 由于spurious wakeup(虚假唤醒)的原因,wait函数返回并不代表条件已经成立,在wait函数返回后,需要再次判断条件是否成立。因此需要将wai调用放到while循环中。

wait端检测到条件不成立时,可以调用以下两个函数进行等待。

1 int pthread_cond_timedwait(pthread_cond_t *restrict cond,
2               pthread_mutex_t *restrict mutex,
3               const struct timespec *restrict abstime);
4 int pthread_cond_wait(pthread_cond_t *restrict cond,
5               pthread_mutex_t *restrict mutex);

这两个函数的区别是,第一个函数可以设置超时时间。这里主要说明第一函数的使用方法和我遇到的问题。timespec指定的是超时时刻的绝对时间,而不是相对时间,因此需要先获取当前时间,然后加上需要等待的时间才能得到用于设置的时间,当前时间通过gettimeofday获取。为简化操作,这里把这些操作封装为一个函数,用于设置以毫秒为单位的相对超时时间。我第一次写的获取绝对时间的函数如下:

复制代码
 1 void get_abstime_wait(int microseconds, struct timespec *abstime)
 2 {
 3   struct timeval tv;
 4   int absmsec;
 5   gettimeofday(&tv, NULL);
 6   absmsec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
 7   absmsec += microseconds;
 8 
 9   abstime->tv_sec = absmsec / 1000;
10   abstime->tv_nsec = absmsec % 1000 * 1000000;
11 }
复制代码

第一个参数指定超时的相对时间,比如传500,表示500毫秒后超时。但是这样写几乎肯定使wait端线程进入busy loop状态。因为这些计算的中间结果,超出了int类型的表示范围,溢出了。添加打印后会发现abstime->tv_sec是一个很小的值,因此调用pthread_cond_timedwait后,立即就超时返回了。因此正确的写法是使用long long来保存结果,long long 类型在linux上是64位的,因此肯定不会溢出。

复制代码
 1 void get_abstime_wait(int microseconds, struct timespec *abstime)
 2 {
 3   struct timeval tv;
 4   long long absmsec;
 5   gettimeofday(&tv, NULL);
 6   absmsec = tv.tv_sec * 1000ll + tv.tv_usec / 1000ll;
 7   absmsec += microseconds;
 8 
 9   abstime->tv_sec = absmsec / 1000ll;
10   abstime->tv_nsec = absmsec % 1000ll * 1000000ll;
11 }
复制代码

使用pthread_cond_timedwait过程中遇到的另一个奇葩问题是,当时间被设置为1970年以前时,超时机制也会出问题。我的工作是搞嵌入式开发,遇到过一台设备的时钟芯片坏掉后,获取出来的时间有问题,导致时间被设置成了1970年以前,这竟然导致pthread_cond_timedwait长眠不醒。本来打算在ubuntu上重现一下,结果系统不让设置1970年以前的时间了,也许已经不支持1970年以前的时间了吧,就先不管了。如果那位大神知道怎么回事,麻烦通知小弟一声,小弟不甚感激。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值