pthread_cond_timedwait()函数阻塞住调用该函数的线程,等待由cond指定的条件被触发(pthread_cond_broadcast() or pthread_cond_signal())。
当pthread_cond_timedwait()被调用时,调用线程必须已经锁住了mutex。函数pthread_cond_timedwait()会对mutex进行【解锁和执行对条件的等待】(原子操作)。这里的原子意味着:解锁和执行条件的等待是原则的,一体的。(In this case, atomically means with respect to the mutex and the condition variable and other access by threads to those objects through the pthread condition variable interfaces.)
如果等待条件满足或超时,或线程被取消,调用线程需要在线程继续执行前先自动锁住mutex,如果没有锁住mutex,产生EPERM错误。即,该函数返回时,mutex已经被调用线程锁住。
等待的时间通过abstime参数(绝对系统时间,过了该时刻就超时)指定,超时则返回ETIMEDOUT错误码。开始等待后,等待时间不受系统时钟改变的影响。
尽管时间通过秒和纳秒指定,系统时间是毫秒粒度的。需要根据调度和优先级原因,设置的时间长度应该比预想的时间要多或者少点。可以通过使用系统时钟接口gettimeofday()获得timeval结构体。
多线程编程中,线程A循环计算,然后sleep一会接着计算(目的是减少CPU利用率);存在的问题是,如果要关闭程序,通常选择join线程A等待线程A退出,可是我们必须等到sleep函数返回,该线程A才能正常退出,这无疑减慢了程序退出的速度。当然,你可以terminate线程A,但这样做很不优雅,且会存在一些未知问题。采用pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t *mutex, const struct timespec * abstime)可以优雅的解决该问题,设置等待条件变量cond,如果超时,则返回;如果等待到条件变量cond,也返回。本文暂不将内部机理,仅演示一个demo
static unsigned char flag = 1;
void * thr_fn(void * arg)
{
while (flag)
{
printf(“thread sleep 10 second\n”);
sleep(10);
}
printf(“thread exit\n”);
}
int main()
{
pthread_t thread;
if (0 != pthread_create(&thread, NULL, thr_fn, NULL))
{
printf(“error when create pthread,%d\n”, errno);
return 1;
}
char c ;
while ((c = getchar()) != ‘q’);
printf(“Now terminate the thread!\n”);
flag = 0;
printf(“Wait for thread to exit\n”);
pthread_join(thread, NULL);
printf(“Bye\n”);
return 0;
}
输入q后,需要等线程从sleep中醒来(由挂起状态变为运行状态),即最坏情况要等10s,线程才会被join。采用sleep的缺点:不能及时唤醒线程。
采用pthread_cond_timedwait函数实现的如下
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
pthread_t thread;
pthread_cond_t cond;
pthread_mutex_t mutex;
unsigned char flag = 1;
void * thr_fn(void * arg)
{
int loop = 0;
struct timeval now;
struct timespec outtime;
pthread_mutex_lock(&mutex);
while (flag)
{
printf("thread[%u] sleep now, loop=%d\n", (unsigned int)pthread_self(), loop);
gettimeofday(&now, NULL);
outtime.tv_sec = now.tv_sec + 1;
outtime.tv_nsec = now.tv_usec * 1000;
pthread_cond_timedwait(&cond, &mutex, &outtime);
loop++;
}
pthread_mutex_unlock(&mutex);
printf("[%s,%d], thread[%u] exit\n", __FUNCTION__, __LINE__, (unsigned int)pthread_self());
}
int main(int argc, char **argv)
{
char c ;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
if (0 != pthread_create(&thread, NULL, thr_fn, NULL))
{
printf("error when create pthread,%d\n", errno);
return 1;
}
printf("please input any key to exit thread[%u]\n", (unsigned int)thread);
while (c = getchar() != 'q')
{
printf("Now terminate the thread!\n");
flag = 0;
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
printf("Wait for thread to exit\n");
pthread_join(thread, NULL);
printf("thread[%u] Bye\n", (unsigned int)thread);
return 0;
}
}
-
thread_function中的循环首先锁定互斥体并且读取标志变量的值。 -
如果标志变量已经被设定,该线程将互斥体解锁然后执行工作函数 -
如果标志没有被设置,该线程自动锁定互斥体并开始等待条件变量的信号
参数是一个指向 pthread_cond_t 类型变量的指针。
The mutex passed to pthread_cond_wait protects the condition.The caller
上面是APUE的原话,就是说pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t*mutex)
函数传入的参数mutex用于保护条件,因为我们在调用pthread_cond_wait时,如果条件不成立我们就进入阻塞,但是进入阻塞这个期间,如果条件变量改变了的话,那我们就漏掉了这个条件。因为这个线程还没有放到等待队列上,所以调用pthread_cond_wait前要先锁互斥量,即调用pthread_mutex_lock(),pthread_cond_wait在把线程放进阻塞队列后,自动对mutex进行解锁,使得其它线程可以获得加锁的权利。这样其它线程才能对临界资源进行访问并在适当的时候唤醒这个阻塞的进程。当pthread_cond_wait返回的时候又自动给mutex加锁。
实际上边代码的加解锁过程如下:
pthread_mutex_lock(&qlock);
pthread_cond_wait(&qready, &qlock);
pthread_mutex_unlock(&qlock);