pthread条件变量
pthread条件变量等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait())的竞争条件。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
1 #include <pthread.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 7 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 8 9 struct node { 10 int n_number; 11 struct node *n_next; 12 } *head = NULL; 13 14 // 线程清理函数 15 static void cleanup_handler(void *arg) 16 { 17 printf("Cleanup handler of second thread.\n"); 18 free(arg); 19 (void)pthread_mutex_unlock(&mtx); 20 } 21 22 static void *thread_func(void *arg) 23 { 24 struct node *p = NULL; 25 26 pthread_cleanup_push(cleanup_handler, p); 27 while (true) { 28 //这个mutex主要是用来保证pthread_cond_wait的并发性 29 pthread_mutex_lock(&mtx); 30 31 /* 32 因为pthread_cond_wait里的线程可能会被意外唤醒, 33 如果这个时候head != NULL,则不是我们想要的情况。 34 这个时候,应该让线程继续进入pthread_cond_wait 35 */ 36 while (head == NULL) { 37 /* 38 pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx, 39 然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立而被唤醒, 40 唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);再读取资源 41 用这个流程是比较清楚的block-->unlock-->wait() return-->lock 42 */ 43 pthread_cond_wait(&cond, &mtx); 44 } 45 p = head; 46 head = head->n_next; 47 printf("Got %d from front of queue\n", p->n_number); 48 free(p); 49 //临界区数据操作完毕,释放互斥锁 50 pthread_mutex_unlock(&mtx); 51 } 52 pthread_cleanup_pop(0); 53 return 0; 54 } 55 56 int main(void) 57 { 58 pthread_t tid; 59 int i; 60 struct node *p; 61 pthread_create(&tid, NULL, thread_func, NULL); 62 63 for (i = 0; i < 3; i++) { 64 p = (struct node*)malloc(sizeof(struct node)); 65 p->n_number = i; 66 67 // 加锁->signal->解锁 68 pthread_mutex_lock(&mtx); 69 p->n_next = head; 70 head = p; 71 pthread_cond_signal(&cond); 72 pthread_mutex_unlock(&mtx); 73 sleep(1); 74 } 75 printf("cancel thread\n"); 76 /* 77 从外部终止子线程,子线程会在最近的取消点,退出线程 78 最近的取消点肯定就是pthread_cond_wait()了。 79 */ 80 pthread_cancel(tid); 81 // 等待cancel完成,不然可能不会执行到cleanup。 82 pthread_join(tid, NULL); 83 printf("All done -- exiting\n"); 84 return 0; 85 }
pthread_cleanup_push来注册清理函数rtn,这个函数有一个参数arg。在以下三种情形之一发生时,注册的清理函数被执行:
1)调用pthread_exit。
2)作为对取消线程请求(pthread_cancel)的响应。
3)以非0参数调用pthread_cleanup_pop。
注意:
1)如果线程只是由于简单的返回而终止的,则清除函数不会被调用。
2)如果pthread_cleanup_pop被传递0参数,则清除函数不会被调用,但是会清除处于栈顶的清理函数。