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

线程等待和唤醒函数比较

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

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

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

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

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

函数定义如下

//函数涉及的变量
typedef struct ThreadSignal_T
{
BOOL relativeTimespan; //是否采用相对时间 pthread_cond_t cond;
pthread_mutex_t mutex; pthread_condattr_t cattr; } ThreadSignal; //初始化
void ThreadSignal_Init(ThreadSignal *signal,BOOL relativeTimespan);
//关闭
void ThreadSignal_Close(ThreadSignal *signal); //等待n毫秒
void ThreadSignal_Wait(ThreadSignal *signal, int ms);
//唤醒线程
void ThreadSignal_Signal(ThreadSignal *signal);

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

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

2) ThreadSignal_Close

void ThreadSignal_Close(ThreadSignal *signal)
{
if (signal->relativeTimespan)
{
pthread_condattr_destroy(&(signal->cattr));
} pthread_mutex_destroy(&signal->mutex);
pthread_cond_destroy(&signal->cond);
}

3) ThreadSignal_Wait


void ThreadSignal_Wait(ThreadSignal *signal, int ms)
{
pthread_mutex_lock(&signal->mutex); if (signal->relativeTimespan)
{
//获取时间
struct timespec outtime;
clock_gettime(CLOCK_MONOTONIC, &outtime);
//ms为毫秒,换算成秒
outtime.tv_sec += ms/; //在outtime的基础上,增加ms毫秒
//outtime.tv_nsec为纳秒,1微秒=1000纳秒
//tv_nsec此值再加上剩余的毫秒数 ms%1000,有可能超过1秒。需要特殊处理
uint64_t us = outtime.tv_nsec/ + * (ms % ); //微秒
//us的值有可能超过1秒,
outtime.tv_sec += us / ; us = us % ;
outtime.tv_nsec = us * ;//换算成纳秒 int ret = pthread_cond_timedwait(&signal->cond, &signal->mutex, &outtime);
}
else
{
struct timeval now;
gettimeofday(&now, NULL); //在now基础上,增加ms毫秒
struct timespec outtime;
outtime.tv_sec = now.tv_sec + ms / ; //us的值有可能超过1秒
uint64_t us = now.tv_usec + * (ms % );
outtime.tv_sec += us / ; us = us % ;
outtime.tv_nsec = us * ; int ret = pthread_cond_timedwait(&signal->cond, &signal->mutex, &outtime);
}
pthread_mutex_unlock(&signal->mutex);
}

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

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

4)ThreadSignal_Signal

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

linux平台,对线程等待和唤醒操作的封装(pthread_cond_timedwait 用法详解)的更多相关文章

  1. WordPress 数据库操作WPDB对象($wpdb)用法详解【转载】

    使用wordpress的时候,如果想直接使用WP里封装的数据库操作的类(wp-db.php),将wp-blog-header.php包含到代码中就可以使用了. define(‘PATH’, dirna ...

  2. PHP操作Excel – PHPExcel 基本用法详解

    导出excel属性设置//Include classrequire_once('Classes/PHPExcel.php');require_once('Classes/PHPExcel/Writer ...

  3. java 多线程—— 线程等待与唤醒

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  4. java - 线程等待与唤醒

    Java多线程系列--“基础篇”05之 线程等待与唤醒 概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. w ...

  5. Java 多线程基础(六)线程等待与唤醒

    Java 多线程基础(六)线程等待与唤醒 遇到这样一个场景,当某线程里面的逻辑需要等待异步处理结果返回后才能继续执行.或者说想要把一个异步的操作封装成一个同步的过程.这里就用到了线程等待唤醒机制. 一 ...

  6. 转:一个跨WINDOWS LINUX平台的线程类

     来源:http://blog.csdn.net/dengxu11/article/details/7232681 继Windows下实现一个CThread封装类之后,这里我再实现一个跨WINDOWS ...

  7. JUC在深入面试题——三种方式实现线程等待和唤醒(wait/notify,await/signal,LockSupport的park/unpark)

    一.前言 在多线程的场景下,我们会经常使用加锁,来保证线程安全.如果锁用的不好,就会陷入死锁,我们以前可以使用Object的wait/notify来解决死锁问题.也可以使用Condition的awai ...

  8. kafka实战教程(python操作kafka),kafka配置文件详解

    kafka实战教程(python操作kafka),kafka配置文件详解 应用往Kafka写数据的原因有很多:用户行为分析.日志存储.异步通信等.多样化的使用场景带来了多样化的需求:消息是否能丢失?是 ...

  9. linux curl用法详解

    linux ‍‍curl用法详解 ‍‍curl的应用方式,一是可以直接通过命令行工具,另一种是利用libcurl库做上层的开发.本篇主要总结一下命令行工具的http相关的应用, 尤其是http下载方面 ...

随机推荐

  1. json与字典的区别

  2. django rest framework(3)

    目录 一.版本 二.解析器 三.序列化 四.请求数据验证 一.版本 程序也来越大时,可能通过版本不同做不同的处理 没用rest_framework之前,我们可以通过以下这样的方式去获取. class ...

  3. hdu1565 用搜索代替枚举找可能状态或者轮廓线解(较优),参考poj2411

    这题用直接枚举是超时的,必须要用搜索来搜索出所有可能的状态,然后再进行枚举 这是较慢的做法 /* 方格取数,相邻格子的数不可取,问最多取到的和是什么 有点类似炮兵布阵,先打出所有可能的状态,然后dp[ ...

  4. python+selenium十四:xpath和contains模糊匹配

    xpath可以以标签定位,也可以@任意属性: 如:以input标签定位:driver.find_element_by_xpath("//input[@id='kw']") 如:@t ...

  5. vue 在.vue文件里监听路由

    监听路由  watch   $route vue项目中的App.vue 文件 <template> <div id="app"> <!--includ ...

  6. binlog2sql之MySQL数据闪回实践

    DBA或开发人员,有时会误删或者误更新数据,如果是线上环境并且影响较大,就需要能快速回滚.传统恢复方法是利用备份重搭实例,再应用去除错误sql后的binlog来恢复数据.此法费时费力,甚至需要停机维护 ...

  7. jmeter csv中获取带引号的数据详情

    最近在工作中,对jmeter实践的点滴的记录这里分享,不一定正确,仅供参考和讨论,有想法的欢迎留言.谈论. 1技巧1:从csv中获取带引号的数据详情 背景:我们从csv中获取数据,在jmeter中使用 ...

  8. C和C++ 中的const

    C++中的const正常情况下是看成编译期的常量,编译器并不为const分配空间,只是在编译的时候将期值保存在名字表中,并在适当的时候折合在代码中.所以,以下代码: #include <iost ...

  9. MVC开发中的常见错误-02-在应用程序配置文件中找不到名为“OAEntities”的连接字符串。

    在应用程序配置文件中找不到名为“OAEntities”的连接字符串. 分析原因:由于Model类是数据库实体模型,通过从数据库中引用的方式添加实体,所以会自动产生一个数据库连接字符串,而程序运行到此, ...

  10. P1036 选数 题解

    题目链接https://www.luogu.org/problemnew/show/P1036 题目描述 已知 nnn 个整数 x1,x2,-,xnx_1,x_2,-,x_nx1​,x2​,-,xn​ ...