pthread_cond_timedwait()函数和pthread_cond_wait()函数,其实2个函数都差不多,我主要是要用pthread_cond_timedwait()函数。
pthread_cond_timedwait()函数有三个入口参数:
(1)pthread_cond_t __cond:条件变量(触发条件)
(2)pthread_mutex_t __mutex: 互斥锁
(3)struct timespec __abstime: 等待时间(其值为系统时间 + 等待时间)
当在指定时间内有信号传过来时,pthread_cond_timedwait()返回0,否则返回一个非0数(我没有找到返回值的定义);
在使用pthread_cond_timedwait()函数时,必须有三步:
1:加互斥锁:pthread_mutex_lock(&__mutex)
2:等待:pthread_cond_timedwait(&__cond, &__mutex, &__abstime) //解锁->等待->加锁
3:解互斥锁:pthread_mutex_unlock(&__mutex)
发送信号量时,也要有三步:
1:加互斥锁:pthread_mutex_lock(&__mutex)
2:发送:pthread_cond_signal(&__cond)
3:解互斥锁:pthread_mutex_unlock(&__mutex)
那么,这里就有一个问题,等待的时候已经加上锁了,那么我发送的时候怎么才能运行到发送函数呢?其实这是因为在pthread_cond_timedwait()函数中已经对互斥锁进行解锁操作了,所以这个时候发送信号量是不会阻塞的。其实仔细想想,这样不是才能保证同步吗?(写完代码后考虑一下)
#include
#include
#include
#include
#include
#include
#define SENDSIGTIME 10
pthread_cond_t g_cond;
pthread_mutex_t g_mutex;
void thread1(void *arg)
{
int inArg = (int)arg;
int ret = 0;
struct timeval now;
struct timespec outtime;
pthread_mutex_lock(&g_mutex);
gettimeofday(&now, NULL);
outtime.tv_sec = now.tv_sec + 5;
outtime.tv_nsec = now.tv_usec * 1000;
ret = pthread_cond_timedwait(&g_cond, &g_mutex, &outtime);
//ret = pthread_cond_wait(&g_cond, &g_mutex);
pthread_mutex_unlock(&g_mutex);
printf("thread 1 ret: %d\n", ret);
}
int main(void)
{
pthread_t id1;
int ret;
pthread_cond_init(&g_cond, NULL);
pthread_mutex_init(&g_mutex, NULL);
ret = pthread_create(&id1, NULL, (void *)thread1, (void *)1);
if (0 != ret)
{
printf("thread 1 create failed!\n");
return 1;
}
printf("等待%ds发送信号!\n", SENDSIGTIME);
sleep(SENDSIGTIME);
printf("正在发送信号....\n");
pthread_mutex_lock(&g_mutex);
pthread_cond_signal(&g_cond);
pthread_mutex_unlock(&g_mutex);
pthread_join(id1, NULL);
pthread_cond_destroy(&g_cond);
pthread_mutex_destroy(&g_mutex);
return 0;
}
线程等待和唤醒函数比较
平台提供了线程等待相关函数,这些函数之间用法也有些差异:
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)
{
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/1000;
//在outtime的基础上,增加ms毫秒
//outtime.tv_nsec为纳秒,1微秒=1000纳秒
//tv_nsec此值再加上剩余的毫秒数 ms%1000,有可能超过1秒。需要特殊处理
uint64_t us = outtime.tv_nsec/1000 + 1000 * (ms % 1000); //微秒
//us的值有可能超过1秒,
outtime.tv_sec += us / 1000000;
us = us % 1000000;
outtime.tv_nsec = us * 1000;//换算成纳秒
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 / 1000;
//us的值有可能超过1秒
uint64_t us = now.tv_usec + 1000 * (ms % 1000);
outtime.tv_sec += us / 1000000;
us = us % 1000000;
outtime.tv_nsec = us * 1000;
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);
}