在通常情况下,程序中的多个线程会并发执行,每个线程处理各自的任务,直到其调用 pthread_exit()
函数或从线程启动函数中返回。但有时候也会用到线程的取消,即向一个线程发送一个请求,要求其立即退出。例如,一组线程正在执行一个任务,如果某个线程检测到错误发生,需要其他线程退出,此时就需要取消线程的功能。
设置线程取消状态
pthread_cancle()
函数向由 thread
指定的线程发送一个取消请求。发送取消请求后,函数 pthread_cancel()
立即返回,不会等待目标线程退出。
#include <pthread.h> int pthread_cancel(pthread_t thread);点击复制复制失败已复制
那么此时目标线程发生的结果及发生的时间取决于线程的取消状态和类型。
#include <pthread.h> int pthread_setcancelstate(int state, int *oldstate);点击复制复制失败已复制
pthread_setcancelstate()
函数会将调用线程的取消状态设置为参数 state
所给定的值。参数 state
可被设置为 PTHREAD_CANCEL_DISABLE
(线程不可取消),如果此类线程收到取消请求,则会将请求挂起,直至将线程的取消状态设置为启用。也可被设置为 PTHREAD_CANCEL_ENABLE
(线程可以被取消)。一般,新创建的线程默认为可以取消。参数 oldstate
用以保存前一次状态。具体示例如下所示,线程执行死循环,并被设置为不可取消状态,则执行取消请求,无任何结果。
#include <pthread.h> #include <stdio.h> #define errlog(errmsg) \ do { \ perror(errmsg); \ printf("--%s--%s--%d--\n", __FILE__, __FUNCTION__, __LINE__); \ return -1; \ } while (0) void *thread_handler(void *arg) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); while (1) { printf("thread...\n"); sleep(1); } pthread_exit(0); } int main(int argc, const char *argv[]) { pthread_t thread; void *retval; if (pthread_create(&thread, NULL, thread_handler, NULL) != 0) { errlog("pthread_create error"); } sleep(3); pthread_cancel(thread); pthread_join(thread, NULL); return 0; }点击复制复制失败已复制
编译并运行,结果如下所示:
$ gcc main.c -lpthread && ./a.out thread... thread... thread... thread... thread... thread... ^C点击复制复制失败已复制
可以看出线程进入死循环,不会被取消请求退出。
设置线程取消类型
pthread_setcanceltype()
如果需要设置线程为可取消状态,则可以选择取消线程。
#include <pthread.h> int pthread_setcanceltype(int type, int *oldtype);点击复制复制失败已复制
pthread_setcanceltype()
函数用以设置当前线程的可取消的类型,上一次的取消类型保存在参数 oldtype
中。参数 type
可以被设置为 PTHREAD_CANCEL_DEFERRED
,表示线程接收取消操作后,直到运行到“可取消点”后取消。 type
也可以被设置为 PTHREAD_CANCEL_ASYNCHRONOUS
,表示接收取消操作后,立即取消。
具体使用如下所示,设置线程状态为可取消状态,并设置取消类型为 PTHREAD_CANCEL_DEFERRED
,本例中程序未设置取消点。
#include <pthread.h> #include <stdio.h> #define errlog(errmsg) \ do { \ perror(errmsg); \ printf("--%s--%s--%d--\n", __FILE__, __FUNCTION__, __LINE__); \ return -1; \ } while (0) void *thread_handler(void *arg) { pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); while (1) { printf("thread...\n"); sleep(1); } pthread_exit(0); } int main(int argc, const char *argv[]) { pthread_t thread; void *retval; if (pthread_create(&thread, NULL, thread_handler, NULL) != 0) { errlog("pthread_create error"); } sleep(3); pthread_cancel(thread); pthread_join(thread, NULL); return 0; }点击复制复制失败已复制
编译并运行,结果如下:
$ gcc main.c -lpthread && ./a.out thread... thread... thread...点击复制复制失败已复制
3
秒之后,程序被取消并退出。注意,此时 pthread_join()
函数不需要第二个参数来回收线程退出时的返回值。因为线程被异常取消之后,无法确定其返回值,否则程序执行会造成段错误。
如果不对线程取消类型进行设置,则线程默认的设置为 PTHREAD_CANCEL_ENABLE
和 PTHREAD_CANCEL_DEFERRED
,即线程可被取消,并且在“取消点”后取消。
pthread_testcancel()
#include <pthread.h> void pthread_testcancel(void);点击复制复制失败已复制
pthread_testcancel()
函数用来给当前线程设置一个“可取消点”。其使用如下所示:线程设置为可取消状态,且设置取消状态为 PTHREAD_CANCEL_DEFERRED
,同时设置取消点。
#include <pthread.h> #include <stdio.h> #define errlog(errmsg) \ do { \ perror(errmsg); \ printf("--%s--%s--%d--\n", __FILE__, __FUNCTION__, __LINE__); \ return -1; \ } while (0) void *thread_handler(void *arg) { pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); while (1) { printf("thread\n"); pthread_testcancel(); printf("thread...\n"); } pthread_exit(0); } int main(int argc, const char *argv[]) { pthread_t thread; void *retval; if (pthread_create(&thread, NULL, thread_handler, NULL) != 0) { errlog("pthread_create error"); } pthread_cancel(thread); pthread_join(thread, NULL); return 0; }点击复制复制失败已复制
编译并运行,结果如下所示:
$ gcc main.c -lpthread && ./a.out thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread thread... thread点击复制复制失败已复制
可以看出线程在取消点取消了。
如果将上例代码中线程的取消类型设置为立即取消,即参数设置为 PTHREAD_CANCEL_ASYNCHRONOUS
,线程立即结束。