文章目录
- 线程取消的两个维度
- 取消状态(State)
- 取消类型(Type)
- 推迟取消(DEFERRED)
- 异步取消(ASYNCHRONOUS)
- 取消点
- 作用
- 示例
- 清理函数
- 函数接口
- 执行时机
- 解决异步取消的死锁问题
- 例程:5秒内取消线程的作业
- 问题:
- 解决方案1:添加取消点
- 解决方案2:使用异步取消(需要清理函数)
- 线程取消是一种请求终止另一个线程执行的机制
- 类似于进程的信号机制,但是专门用于线程间通信
intpthread_cancel(pthread_tthread);// 发送取消请求重要:pthread_cancel()只是发送请求,线程是否终止、何时终止取决于线程自身的取消设置
线程取消的两个维度
取消状态(State)
- 是否接受取消请求
intpthread_setcancelstate(intstate,int*oldstate);state可以是以下两个值之一:PTHREAD_CANCEL_ENABLE:接受取消请求(默认)PTHREAD_CANCEL_DISABLE:拒绝取消请求
oldstate:- 指向一个 int 类型变量的指针,用于保存线程之前的取消状态
- 如果不需要保存旧状态,可以将其设置为
NULL
取消类型(Type)
- 如何响应取消请求
intpthread_setcanceltype(inttype,int*oldtype);type是两个值之一:PTHREAD_CANCEL_DEFERRED:推迟到取消点(默认)PTHREAD_CANCEL_ASYNCHRONOUS:立即响应(异步)
oldtype和oldstate类似,它保存线程之前的取消类型,如果不需要保存旧类型,也是将其设置为NULL
推迟取消(DEFERRED)
- 安全,只在预定义的安全点检查取消
// 线程函数void*thread_func(void*arg){pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);// 默认while(1){printf("Working...\n");// 取消点:检查取消请求// 如果在这里收到取消请求,会在printf处响应for(inti=0;i<1000000;i++);// 非取消点,不会响应}returnNULL;}异步取消(ASYNCHRONOUS)
- 立即响应,但可能导致资源泄漏和死锁
void*thread_func(void*arg){pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);pthread_mutex_lock(&mutex);// 加锁glob++;// 修改共享数据// 可能在这里被立即取消!导致锁未释放!pthread_mutex_unlock(&mutex);// 可能永远执行不到returnNULL;}取消点
- 取消点是线程检查是否有取消请求的特定位置
- 只有在这些位置,线程才会响应推迟的取消请求
作用
- 保证线程在安全的状态下被取消
- 避免资源泄漏和数据不一致
// 阻塞型系统调用:read(),write(),accept(),connect()sleep(),usleep(),nanosleep()pthread_cond_wait(),pthread_cond_timedwait()pthread_join(),sem_wait()// 标准I/O函数:printf(),scanf(),fgets(),fread()// 专门函数:pthread_testcancel()// 手动创建取消点示例
void*thread_func(void*arg){for(inti=0;i<10;i++){printf("Running: %d\n",i);// printf是取消点// 如果没有取消点函数,线程不会检查取消请求for(longj=0;j<1000000000;j++);// 纯循环,不是取消点}returnNULL;}清理函数
- 异步取消或在取消点被取消时,线程可能持有资源(锁、内存等)
- 清理函数确保这些资源被正确释放
函数接口
voidpthread_cleanup_push(void(*routine)(void*),void*arg);voidpthread_cleanup_pop(intexecute);执行时机
voidcleanup(void*arg){printf("清理资源: %s\n",(char*)arg);}void*thread_func(void*arg){char*resource=malloc(100);pthread_cleanup_push(cleanup,resource);// 压入栈// 以下情况会执行清理函数:// 1. pthread_cancel() 取消线程// 2. pthread_exit() 线程主动退出// 3. pthread_cleanup_pop(1) 显式弹出并执行if(error){pthread_exit(NULL);// 执行清理函数}pthread_cleanup_pop(0);// 弹出但不执行(正常情况)free(resource);// 正常释放returnNULL;}解决异步取消的死锁问题
- 问题:
void*thread_func(void*arg){pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);pthread_mutex_lock(&mutex);// 加锁glob++;// 可能在这里被取消!sleep(4);// 长时间操作pthread_mutex_unlock(&mutex);// 永远执行不到 → 死锁!returnNULL;}- 解决方案:使用清理函数
voidunlock_mutex(void*arg){pthread_mutex_t*mutex=(pthread_mutex_t*)arg;pthread_mutex_unlock(mutex);printf("清理:解锁互斥量\n");}void*thread_func(void*arg){pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);pthread_mutex_lock(&mutex);pthread_cleanup_push(unlock_mutex,&mutex);// 注册清理函数glob++;// 即使在这里被取消,清理函数也会执行sleep(4);pthread_cleanup_pop(1);// 弹出并执行清理函数(正常流程也执行)// pthread_mutex_unlock(&mutex); // 不再需要,清理函数已处理returnNULL;}例程:5秒内取消线程的作业
问题:
// 线程函数没有取消点,即使发送取消请求也不会响应void*ThreadFunc(void*arg){intloops=*((int*)arg);for(intj=0;j<loops;j++){pthread_mutex_lock(&mutex);glob++;pthread_mutex_unlock(&mutex);// 没有取消点!}pthread_exit(NULL);}解决方案1:添加取消点
void*ThreadFunc(void*arg){intloops=*((int*)arg);// 启用取消pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);for(intj=0;j<loops;j++){pthread_mutex_lock(&mutex);glob++;pthread_mutex_unlock(&mutex);// 定期添加取消点if(j%1000==0){pthread_testcancel();// 手动创建取消点}}pthread_exit(NULL);}// 主线程:5秒后取消intmain(){// ... 创建线程sleep(5);pthread_cancel(tid1);pthread_cancel(tid2);// ...}解决方案2:使用异步取消(需要清理函数)
voidcleanup_unlock(void*arg){pthread_mutex_unlock(&mutex);}void*ThreadFunc(void*arg){intloops=*((int*)arg);pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);for(intj=0;j<loops;j++){pthread_mutex_lock(&mutex);pthread_cleanup_push(cleanup_unlock,NULL);glob++;pthread_cleanup_pop(1);// 弹出并执行(正常流程)// 如果被取消,清理函数也会自动执行}pthread_exit(NULL);}