当pthread_cond_timedwait()被调用时,调用线程必须已经锁住了mutex。函数pthread_cond_timedwait()会对mutex进行【解锁和执行对条件的等待】(原子操作)。这里的原子意味着:解锁和执行条件的等待是原则的,一体的。(Inthis case, atomically means with respect to the mutex and thecondition variable and other access by threads to those objectsthrough 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 unsignedchar flag =1;
- void *thr_fn(void *arg)
- {
- while(flag)
- {
- printf(“thread sleep 10second\n”);
- sleep(10);
- }
- printf(“threadexit\n”);
- }
- intmain()
- {
- 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 toexit\n”);
- pthread_join(thread,NULL);
- printf(“Bye\n”);
- return 0;
- }
输入q后,需要等线程从sleep中醒来(由挂起状态变为运行状态),即最坏情况要等10s,线程才会被join。采用sleep的缺点:不能及时唤醒线程。
采用pthread_cond_timedwait函数实现的如下
- include
- include
- include
- include
- include
- pthread_t thread;
- pthread_cond_t cond;
- pthread_mutex_t mutex;
- unsigned char flag =1;
- void *thr_fn(void *arg)
- {
- struct timevalnow;
- struct timespecouttime;
- pthread_mutex_lock(&mutex);
- while(flag)
- {
- printf(“thread sleepnow\n”);
- gettimeofday(&now, NULL);
- outtime.tv_sec = now.tv_sec + 10;
- outtime.tv_nsec = now.tv_usec * 1000;
- pthread_cond_timedwait(&cond, &mutex,&outtime);
- }
- pthread_mutex_unlock(&mutex);
- printf(“threadexit\n”);
- }
- intmain()
- {
- 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;
- }
- 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 toexit\n”);
- pthread_join(thread,NULL);
- printf(“Bye\n”);
- return 0;
- }
-
thread_function中的循环首先锁定互斥体并且读取标志变量的值。 -
如果标志变量已经被设定,该线程将互斥体解锁然后执行工作函数 -
如果标志没有被设置,该线程自动锁定互斥体并开始等待条件变量的信号
参数是一个指向 pthread_cond_t 类型变量的指针。
The mutex passed to pthread_cond_wait protects the condition.Thecaller
上面是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);
最近看《UNIX环境高级编程》多线程同步,看到他举例说条件变量pthread_cond_t怎么用,愣是没有看懂,只好在网上找了份代码,跑了跑,才弄明白
- #include
- #include
- #include
- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- pthread_cond_t cond =PTHREAD_COND_INITIALIZER;
- void *thread1(void*);
- void *thread2(void*);
- int i=1;
- int main(void)
- {
- pthread_t t_a;
- pthread_t t_b;
- pthread_create(&t_a,NULL,thread1,(void*)NULL);
- pthread_create(&t_b,NULL,thread2,(void*)NULL);
- pthread_join(t_a, NULL);
- pthread_join(t_b, NULL);
- pthread_mutex_destroy(&mutex);
- pthread_cond_destroy(&cond);
- exit(0);
- }
- void *thread1(void*junk)
- {
- for(i=1;i<=6;i++)
- {
- pthread_mutex_lock(&mutex);
- printf("thread1:lock %d/n", __LINE__);
- if(i%3==0){
- printf("thread1:signal 1 %d/n", __LINE__);
- pthread_cond_signal(&cond);
- printf("thread1:signal 2 %d/n", __LINE__);
- sleep(1);
- }
- pthread_mutex_unlock(&mutex);
- printf("thread1:unlock %d/n/n", __LINE__);
- sleep(1);
- }
- }
- void *thread2(void*junk)
- {
- while(i<6)
- {
- pthread_mutex_lock(&mutex);
- printf("thread2: lock %d/n", __LINE__);
- if(i%3!=0){
- printf("thread2: wait 1 %d/n", __LINE__);
- pthread_cond_wait(&cond,&mutex);
- printf("thread2: wait 2 %d/n", __LINE__);
- }
- pthread_mutex_unlock(&mutex);
- printf("thread2:unlock %d/n/n", __LINE__);
- sleep(1);
- }
- }
- #include
- #include
- #include
- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- pthread_cond_t cond =PTHREAD_COND_INITIALIZER;
- void*thread1(void *);
- void*thread2(void *);
- int i=1;
- int main(void)
- {
- pthread_t t_a;
- pthread_t t_b;
- pthread_create(&t_a,NULL,thread1,(void*)NULL);
- pthread_create(&t_b,NULL,thread2,(void*)NULL);
- pthread_join(t_a, NULL);
- pthread_join(t_b, NULL);
- pthread_mutex_destroy(&mutex);
- pthread_cond_destroy(&cond);
- exit(0);
- }
- void*thread1(void*junk)
- {
- for(i=1;i<=6;i++)
- {
- pthread_mutex_lock(&mutex);
- printf("thread1: lock %d/n",__LINE__);
- if(i%3==0){
- printf("thread1:signal 1 %d/n",__LINE__);
- pthread_cond_signal(&cond);
- printf("thread1:signal 2 %d/n",__LINE__);
- sleep(1);
- }
- pthread_mutex_unlock(&mutex);
- printf("thread1: unlock %d/n/n",__LINE__);
- sleep(1);
- }
- }
- void*thread2(void*junk)
- {
- while(i<6)
- {
- pthread_mutex_lock(&mutex);
- printf("thread2: lock %d/n",__LINE__);
- if(i%3!=0){
- printf("thread2: wait 1 %d/n",__LINE__);
- pthread_cond_wait(&cond,&mutex);
- printf("thread2: wait 2 %d/n",__LINE__);
- }
- pthread_mutex_unlock(&mutex);
- printf("thread2: unlock %d/n/n",__LINE__);
- sleep(1);
- }
- }
编译:
[X61@horizon threads]$ gcc thread_cond.c -lpthread -o tcd
以下是程序运行结果:
[X61@horizon threads]$ ./tcd
thread1: lock 30
thread1: unlock 40
thread2: lock 52
thread2: wait 1 55
thread1: lock 30
thread1: unlock 40
thread1: lock 30
thread1:signal 1 33
thread1:signal 2 35
thread1: unlock 40
thread2: wait 2 57
thread2: unlock 61
thread1: lock 30
thread1: unlock 40
thread2: lock 52
thread2: wait 1 55
thread1: lock 30
thread1: unlock 40
thread1: lock 30
thread1:signal 1 33
thread1:signal 2 35
thread1: unlock 40
thread2: wait 2 57
thread2: unlock 61
这里的两个关键函数就在pthread_cond_wait和pthread_cond_signal函数。
本例中:
线程一先执行,获得mutex锁,打印,然后释放mutex锁,然后阻塞自己1秒。
线程二此时和线程一应该是并发的执行,这里是一个要点,为什么说是线程此时是并发的执行,因为此时不做任何干涉的话,是没有办法确定是线程一先获得执行还是线程二先获得执行,到底那个线程先获得执行,取决于操作系统的调度,想刻意的让线程2先执行,可以让线程2一出来,先sleep一秒。
这里并发执行的情况是,线程一先进入循环,然后获得锁,此时估计线程二执行,阻塞在
pthread_mutex_lock(&mutex);
这行语句中,直到线程1释放mutex锁
pthread_mutex_unlock(&mutex);
然后线程二得已执行,获取metux锁,满足if条件,到pthread_cond_wait(&cond,&mutex);
这里的线程二阻塞,不仅仅是等待cond变量发生改变,同时释放mutex锁,因为当时看书没有注意,所以这里卡了很久。
mutex锁释放后,线程1终于获得了mutex锁,得已继续运行,当线程1的if(i%3==0)的条件满足后,通过pthread_cond_signal发送信号,告诉等待cond的变量的线程(这个情景中是线程二),cond条件变量已经发生了改变。
不过此时线程二并没有立即得到运行,因为线程二还在等待mutex锁的释放,所以线程一继续往下走,直到线程一释放mutex锁,线程二才能停止等待,打印语句,然后往下走通过pthread_mutex_unlock(&mutex)释放mutex锁,进入下一个循环。