linux平台,对线程等待和唤醒操作的封装(涉及pthread_cond_timedwait)

释放双眼,带上耳机,听听看~!
00:00
00:00

前言 linux平台下,线程等待和唤醒操作是很常见的,但是平台函数不易使用;笔者对此操作做了封装,使之更易于使用。

线程等待和唤醒函数比较

 平台提供了线程等待相关函数,这些函数之间用法也有些差异:

sleep 线程等待,等待期间线程无法唤醒。
pthread_cond_wait  线程等待信号触发,如果没有信号触发,无限期等待下去。
pthread_cond_timedwait  线程等待一定的时间,如果超时或有信号触发,线程唤醒。

  通过上表,可以看出pthread_cond_timedwait函数是最为灵活,使用也最为广泛。sleep的缺陷是当有紧急事件到达时,线程无法及时唤醒。pthread_cond_wait缺陷是:必须借助别的线程触发信号,否则线程自身无法唤醒,如果使用函数,线程无法处理定时任务。

  一般情况下,线程要做的工作可能有:定期处理某个事物;无事可做时,线程挂起;有事可做时,立即唤醒工作。要完成上面所述的功能,必须用pthread_cond_timedwait函数,本文介绍的就是对该函数封装。

  线程唤醒操作还涉及互斥量pthread_mutex_t,感觉与我们理解的等待和唤醒操作无关;此函数的引入,增加了理解难度。本文封装完全屏蔽了此概念。

函数定义如下

  1. //函数涉及的变量
  2. typedef struct ThreadSignal_T
  3. {
  4. BOOL relativeTimespan; //是否采用相对时间
  5. pthread_cond_t cond;
  6. pthread_mutex_t mutex;
  7. pthread_condattr_t cattr;
  8. } ThreadSignal;
  9. //初始化
  10. void ThreadSignal_Init(ThreadSignal *signal,BOOL relativeTimespan);
  11. //关闭
  12. void ThreadSignal_Close(ThreadSignal *signal);
  13. //等待n毫秒
  14. void ThreadSignal_Wait(ThreadSignal *signal, int ms);
  15. //唤醒线程
  16. void ThreadSignal_Signal(ThreadSignal *signal);

上述函数定义非常直观,利于理解。但是平台提供的函数,就不是那么直观。我把上述函数的实现一一列出来。

  1. 1ThreadSignal_Init
  1. void ThreadSignal_Init(ThreadSignal *signal, BOOL relativeTimespan)
  2. {
  3. //relativeTimespan 是不是采用相对时间等待。参见函数 ThreadSignal_Wait
  4. signal->relativeTimespan = relativeTimespan;
  5. pthread_mutex_init(&signal->mutex, NULL);
  6. if (relativeTimespan)
  7. {
  8. //如果采用相对时间等待,需要额外的处理。
  9. //采用相对时间等待。可以避免:因系统调整时间,导致等待时间出现错误。
  10. int ret = pthread_condattr_init(&signal->cattr);
  11. ret = pthread_condattr_setclock(&signal->cattr, CLOCK_MONOTONIC);
  12. ret = pthread_cond_init(&signal->cond, &signal->cattr);
  13. }
  14. else
  15. {
  16. pthread_cond_init(&signal->cond, NULL);
  17. }
  18. }

2) ThreadSignal_Close

  1. void ThreadSignal_Close(ThreadSignal *signal)
  2. {
  3. if (signal->relativeTimespan)
  4. {
  5. pthread_condattr_destroy(&(signal->cattr));
  6. }
  7. pthread_mutex_destroy(&signal->mutex);
  8. pthread_cond_destroy(&signal->cond);
  9. }

3) ThreadSignal_Wait

  1.  
  1. void ThreadSignal_Wait(ThreadSignal *signal, int ms)
  2. {
  3. pthread_mutex_lock(&signal->mutex);
  4. if (signal->relativeTimespan)
  5. {
  6. //获取时间
  7. struct timespec outtime;
  8. clock_gettime(CLOCK_MONOTONIC, &outtime);
  9. //ms为毫秒,换算成秒
  10. outtime.tv_sec += ms/1000;
  11. //在outtime的基础上,增加ms毫秒
  12. //outtime.tv_nsec为纳秒,1微秒=1000纳秒
  13. //tv_nsec此值再加上剩余的毫秒数 ms%1000,有可能超过1秒。需要特殊处理
  14. uint64_t us = outtime.tv_nsec/1000 + 1000 * (ms % 1000); //微秒
  15. //us的值有可能超过1秒,
  16. outtime.tv_sec += us / 1000000;
  17. us = us % 1000000;
  18. outtime.tv_nsec = us * 1000;//换算成纳秒
  19.  
  20. int ret = pthread_cond_timedwait(&signal->cond, &signal->mutex, &outtime);
  21. }
  22. else
  23. {
  24. struct timeval now;
  25. gettimeofday(&now, NULL);
  26. //在now基础上,增加ms毫秒
  27. struct timespec outtime;
  28. outtime.tv_sec = now.tv_sec + ms / 1000;
  29. //us的值有可能超过1秒
  30. uint64_t us = now.tv_usec + 1000 * (ms % 1000);
  31. outtime.tv_sec += us / 1000000;
  32. us = us % 1000000;
  33. outtime.tv_nsec = us * 1000;
  34. int ret = pthread_cond_timedwait(&signal->cond, &signal->mutex, &outtime);
  35. }
  36. pthread_mutex_unlock(&signal->mutex);
  37. }
  1.  

上述函数处理起来有点啰嗦,有些读者可能认为这是多此一举,其实不然。 struct timespec outtime;结构中有两个值:tv_sec ,tv_usec 。分别是秒和纳秒。等待一段时间就是:在这两个值上增加一定的数值。

tv_usec 此值有范围限制的,就是不能超过1秒暨1000000000纳秒。如果超出1秒,就要在tv_sec 此值增加一秒;tv_usec 减去一秒。笔者是在实践中发现此问题的,不是无中生有。如果tv_usec 此值溢出,调用pthread_cond_timedwait函数,会立马返回。

4)ThreadSignal_Signal

  1. void ThreadSignal_Signal(ThreadSignal *signal)
  2. {
  3. pthread_mutex_lock(&signal->mutex);
  4. pthread_cond_signal(&signal->cond);
  5. pthread_mutex_unlock(&signal->mutex);
  6. }
  1. 后记 一个简单的信号等待操作,linux下处理起来就如此复杂,远不如c#、java等语言处理起来那么优雅。通过本文介绍的方法,将复杂的问题封装起来,让使用者用起来得心应手,这就是函数封装要达到的目的。
  1.  
站长资讯

QLineEdit拾遗:数据的过滤、验证和补全

2020-11-9 3:47:03

站长资讯

11scrapy

2020-11-9 3:47:05

0 条回复 A文章作者 M管理员
欢迎您,新朋友,感谢参与互动!
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
私信列表
搜索