前言 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. Java 并发类

    java.util.concurrent包里 提供了一批线程安全的类 一. java.util.concurrent.atomic java.util.concurrent.atomic包里的原子处理 ...

  2. 什么是 TCP 拆、粘包?如何解决(Netty)

    前言 记得前段时间我们生产上的一个网关出现了故障. 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送短信. 但这个请求并不是常见的 HTTP ,而是利用 Netty 自定义的协议. 有个 ...

  3. OrCAD Capture CIS 16.6 导出BOM

    OrCAD Capture CIS 16.6 一.选择设计文件:菜单:Tools > Bill of Materials... 二.Bill of Materials > Open in ...

  4. ajax---获取XMLHttpReuquest 对象

    ajax的异步和同步(Asynchronus Javascript and Xml) 同步:一个时间段只能干一件事:即按部就班,一件事一件事的做. 异步:相同的时间段做多件事,同时进行.依靠 XMLH ...

  5. Controller中方法返回值其他类型需要添加jackson依赖

    第一个 第二个: 第三个 https://www.cnblogs.com/codejackanapes/p/5569013.html:json的博客园 springmvc默认的是:2.Jackson: ...

  6. HDU 1695 GCD (莫比乌斯反演模板)

    GCD Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  7. vs2017初学c++环境配置及使用教程

    https://blog.csdn.net/jmpan_sjtu/article/details/79053169 https://blog.csdn.net/luoyu510183/article/ ...

  8. CentOS6.9安装HDFS

    1.安装依赖包 yum install -y gcc openssh-clients 2.升级glib2.14 升级glibc-2.14用到的rpm 下载地址:https://pan.baidu.co ...

  9. tornado中form表单验证详解

    #!/usr/bin/env python# _*_ coding:utf-8 _*_import tornado.webimport tornado.ioloopimport re class Ba ...

  10. 【bzoj4347】[POI2016]Nim z utrudnieniem dp

    题解: 感觉我简直是个傻逼 把题目数据范围看错了.. 然后觉得这题非常的不可做 sigmaai <1e7.... 这题的dp是非常简单的,注意到d很小 f[i][j][k]表示前i个,%d为j, ...