前言 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. Confluence 6 CSS 编辑快速入门

    希望编辑空间的 CSS 样式表: 进入空间后,然后从边栏的底部选择 空间工具(Space tools) > 外观和感觉(Look and Feel) . 然后选择 样式表(Stylesheet) ...

  2. Confluence 6 自定义主面板

    主面板(dashboard)是你 Confluence 站点的默认载入页面.这个页面能够给用户能够找到其他页面的所有必须的工具,重新进入未完成的工作或者快速导航到喜欢的空间和页面 站点的欢迎信息将会在 ...

  3. Guideline 5.2.1 - Legal - Intellectual Property 解决方案

    最近在上架公司公司项目的时候遇到这个问题什么5.2.1 然后去了解发现最近不少人都遇到了这个问题.先说一下 我上架的APP是一个医疗的APP然后说需要什么医疗资质,估计是账号的公司资质不够吧.后面和苹 ...

  4. VUE开发请求本地数据的配置,旧版本dev-server.js,新版本webpack.dev.conf.js

    VUE开发请求本地数据的配置,早期的vue-lic下面有dev-server.js和dev-client.js两文件,请求本地数据在dev-server.js里配置,最新的vue-webpack-te ...

  5. LeetCode(66): 加一

    Easy! 题目描述: 给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组. 最高位数字存放在数组的首位, 数组中每个元素只存储一个数字. 你可以假设除了整数 0 之外,这个整数不会 ...

  6. 【python】控制台输出颜色

    来源:http://www.cnblogs.com/yinjia/p/5559702.html 在开发项目过程中,为了方便调试代码,经常会向stdout中输出一些日志,默认的这些日志就直接显示在了终端 ...

  7. 《剑指offer》 链表中倒数第k个节点

    本题来自<剑指offer> 链表中倒数第k个节点 题目: 输入一个链表,输出该链表中倒数第k个结点. 思路: 倒数第k个节点,而且只能访问一遍链表,定义两个节点,两者之间相差k个距离,遍历 ...

  8. python unittest装载、执行、造成报告

    #执行用例 caselist = config.caselist for i in range(0,len(caselist)): reportname = caselist[i][2:] now = ...

  9. JS实现简单的观察者模式

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. 获取访问IP

    public static String GetIP() { String ip = HttpContext.Current.Request.ServerVariables["HTTP_X_ ...