posix多线程--线程取消
1.三种取消状态
Off 禁用取消
Deferred 推迟取消:在下一个取消点执行取消
Asynchronous 异步取消:可以随时执行取消
int pthread_cancel(pthread_t thread)
2.推迟取消:在下一个取消点执行取消
Pthreads系统上的某些函数会被作为取消点,如pthread_testcancel,sleep,pthread_cond_wait等。
线程调用pthread_cancel函数后,被取消线程不会立即取消,仅仅在到达取消点时响应取消请求。
代码示例如下:
在pthread_testcancel取消点,响应线程取消请求。
#include<stdio.h> #include<pthread.h> #include<errno.h> int counter; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; void *thread_route(void *arg) { pthread_mutex_lock(&mutex); for(counter=0;;counter++) { if(counter%2000==0){ printf("calling testcancel\n"); pthread_testcancel(); } } pthread_mutex_unlock(&mutex); } int main(void) { pthread_t tid; void *result; pthread_create(&tid,NULL,thread_route,NULL); sleep(1); printf("call cancel\n"); pthread_cancel(tid); printf("call joining\n"); pthread_join(tid,&result); if(result==PTHREAD_CANCELED) { printf("Thread cancelled at %d\n",counter); } else{ printf("Thread was not canceled\n"); } pthread_mutex_lock(&mutex); printf("main thread locked"); pthread_mutex_unlock(&mutex); }
3.如果要保证取消不在一个特别的取消点发生,可以暂时在代码的那个区域停用取消。
int pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,int *state)
代码示例如下:
在sleep()时,禁用取消。
#include<stdio.h> #include<pthread.h> #include<errno.h> int counter; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; void *thread_route(void *arg) { int state; pthread_mutex_lock(&mutex); for(counter=0;;counter++) { if(counter%582==0) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&state) ; sleep(1); pthread_setcancelstate(state,&state); } if(counter%2000==0){ printf("calling testcancel\n"); pthread_testcancel(); } } pthread_mutex_unlock(&mutex); } int main(void) { pthread_t tid; void *result; pthread_create(&tid,NULL,thread_route,NULL); sleep(1); printf("call cancel\n"); pthread_cancel(tid); printf("call joining\n"); pthread_join(tid,&result); if(result==PTHREAD_CANCELED) { printf("Thread cancelled at %d\n",counter); } else{ printf("Thread was not canceled\n"); } pthread_mutex_lock(&mutex); printf("main thread locked"); pthread_mutex_unlock(&mutex); }
4.异步取消:可以随时执行取消
异步取消不需要使用取消点来查询取消请求。异步取消不能获得任何资源,应避免异步的取消
代码示例如下:
矩阵相乘线程取消
#include<stdio.h> #include<pthread.h> #define SIZE 10 int arr_a[SIZE][SIZE]; int arr_b[SIZE][SIZE]; int arr_c[SIZE][SIZE]; void printarr(int arr[SIZE][SIZE]) { int i,j; for(i=0;i<SIZE;i++) { for(j=0;j<SIZE;j++) { printf("%x ",arr[i][j]); } printf("\n"); } } void *thread_routine(void *arg) { int i,j,cancel_type; for(i=0;i<SIZE;i++) for(j=0;j<SIZE;j++) { arr_a[i][j] = i; arr_b[i][j] = j; } while(1) { pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&cancel_type); for(i=0;i<SIZE;i++) for(j=0;j<SIZE;j++) { arr_c[i][j] = arr_a[i][j]*arr_b[i][j]; } pthread_setcanceltype(cancel_type,&cancel_type); for(i=0;i<SIZE;i++) for(j=0;j<SIZE;j++) arr_a[i][j] = arr_c[i][j]; } } int main(void) { pthread_t tid; void *result; pthread_create(&tid,NULL,thread_routine,NULL); sleep(1); printf("canceling!"); pthread_cancel(tid); printf("joining"); pthread_join(tid,&result); if(result==PTHREAD_CANCELED) printf("thread cancelled\n"); else printf("thread was not cancelled\n"); printarr(arr_a); printarr(arr_b); printarr(arr_c); }
5.清除
在编写代码时,应将其设计为可以推迟取消,在不适当的地方停用取消,在取消点使用清除处理器。
清除处理器可以理解为每个线程有一个活动的清除处理函数的栈,调用pthread_cleanup_push将清除函数加到栈中,调用pthread_cleanup_pop删除最近增加的处理函数。当所有活动的清除处理函数返回时,线程被终止。当pthread_cleanup_pop以非零值被调用时,即使线程没被取消,清除处理函数也要被执行。
代码示例如下:
当一个条件变量等待被取消时,使用一个清除处理函数来释放互斥量。
#include<stdio.h> #include<pthread.h> #define THREADS 5 typedef struct work_tag { pthread_mutex_t mutex; pthread_cond_t cond; int counter; int busy; }work_t; work_t work = {PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER,0,1}; void cleanup_handler(void *arg) { work_t *work_p = (work_t*)arg; work_p->counter--; pthread_mutex_unlock(&work_p->mutex); } void *thread_routine(void *arg) { pthread_cleanup_push(cleanup_handler,(void *)&work); pthread_mutex_lock(&work.mutex); work.counter++; while(work.busy) { pthread_cond_wait(&work.cond,&work.mutex); } pthread_cleanup_pop(1); return NULL; } int main(void) { pthread_t tid[THREADS]; void *result; int i; for(i=0;i<THREADS;i++) { pthread_create(&tid[i],NULL,thread_routine,NULL); } sleep(2); for(i=0;i<THREADS;i++) { pthread_cancel(tid[i]); pthread_join(tid[i],&result); if(result == PTHREAD_CANCELED) printf("thread %d cancelled\n",i); else printf("thread %d was not cancelled\n",i); } return 0; }
在一套“转包”功能的程序中,当分包线程在进行中时,承包线程被取消,这时不希望分包线程继续运行。可以在承包线程清除处理函数中取消每个分包线程,
如果原来是连接分包线程,它们将继续消费一些资源直到它们被连接或分离。这时应在承包线程清除处理函数中使用pthread_detach立即分离它。
代码示例如下:
#include<stdio.h> #include<pthread.h> #define THREADS 5 typedef struct send_tag { pthread_t sid[THREADS]; }send_t; void *send_routine(void *arg) { int counter; for(counter=0;;counter++) if(counter%1000==0) pthread_testcancel(); } void cleanup(void *arg) { send_t *send = (send_t*)arg; int i; for(i=0;i<THREADS;i++) { pthread_cancel(send->sid[i]); pthread_detach(send->sid[i]); printf("cleanup:cancelled %d\n",i); } } void *thread_routine(void *arg) { send_t send; int i; void *result; for(i=0;i<THREADS;i++) { pthread_create(&send.sid[i],NULL,send_routine,NULL); } pthread_cleanup_push(cleanup,(void*)&send); for(i=0;i<THREADS;i++) pthread_join(send.sid[i],&result); pthread_cleanup_pop(0); } int main(void) { pthread_t tid; void *result; pthread_create(&tid,NULL,thread_routine,NULL); sleep(5); pthread_cancel(tid); pthread_join(tid,&result); return 0; }
参考资料:《POSIX多线程程序设计》 pp.120-137