8.线程取消
8.1线程取消函数接口
int pthread_cancel(pthread_t thread);
一个线程可以通过调用该函数向另一个线程发送取消请求。 这不是个阻塞型接口, 发出请求后, 函数就立刻返回了, 而不会等待目标线程退出之后才返回。
调用pthread_cancel时, 会向目标线程发送一个SIGCANCEL的信号, 该信号就是kill -l中消失的32号信号。
线程的默认取消状态是PTHREAD_CANCEL_ENABLE。即是可被取消的。

什么是取消点? 可通过man pthreads查看取消点
就是对于某些函数, 如果线程允许取消且取消类型是延迟取消, 并且线程也收到了取消请求, 那么当执行到这些函数的时候, 线程就可以退出了。
8.2线程取消带来的弊端
目标线程可能会持有互斥量、 信号量或其他类型的锁, 这时候如果收到取消请求, 并且取消类型是异步取消, 那么可能目标线程掌握的资源还没有来得及释放就被迫退出了, 这可能会给其他线程带来不可恢复的后果, 比如死锁(其他线程再也无法获得资源) 。
注意:
轻易不要调用pthread_cancel函数, 在外部杀死线程是很糟糕的做法,毕竟如果想通知目标线程退出, 还可以采取其他方法。
如果不得不允许线程取消, 那么在某些非常关键不容有失的代码区域, 暂时将线程设置成不可取消状态, 退出关键区域之后, 再恢复成可以取消的状态。
在非关键的区域, 也要将线程设置成延迟取消, 永远不要设置成异步取消。
8.2线程清理函数
假设遇到取消请求, 线程执行到了取消点, 却没有来得及做清理动作(如动态申请的内存没有释放, 申请的互斥量没有解锁等) , 可能会导致错误的产生, 比如死锁, 甚至是进程崩溃。
为了避免这种情况, 线程可以设置一个或多个清理函数, 线程取消或退出时,会自动执行这些清理函数, 以确保资源处于一致的状态。
如果线程被取消, 清理函数则会负责解锁操作。
void pthread_cleanup_push(void (*routine)(void *),void *arg);void pthread_cleanup_pop(int execute);
这两个函数必须同时出现, 并且属于同一个语法块。
何时会触发注册的清理函数:?
1、当线程的主函数是调用pthread_exit返回的, 清理函数总是会被执行。
2、当线程是被其他线程调用pthread_cancel取消的, 清理函数总是会被执行。
3、当线程的主函数是通过return返回的, 并且pthread_cleanup_pop的唯一参数execute是0时, 清理函数不会被执行.
4、线程的主函数是通过return返回的, 并且pthread_cleanup_pop的唯一参数execute是非零值时, 清理函数会执行一次。
代码:
#include #include #include #include #include #include #define NUMBER 2int g_bowl = 0;pthread_mutex_t mutex;//定义互斥锁void clean(void *arg){ printf("Clean up:%sn",(char*)arg); pthread_mutex_unlock(&mutex);//释放锁}void *WorkCancel(void *arg){ pthread_mutex_lock(&mutex); pthread_cleanup_push(clean,"clean up handler");//清除函数的push struct timespec t = {3,0};//取消点 nanosleep(&t,0); pthread_cleanup_pop(0);//清除 pthread_mutex_unlock(&mutex);}void *WorkWhile(void *arg){ sleep(5); pthread_mutex_lock(&mutex); printf("i get the mutexn");//若能拿到资源,则表示取消清理函数成功! pthread_mutex_unlock(&mutex); return NULL;}int main(){ pthread_t cons,prod; pthread_mutex_init(&mutex,NULL);//互斥锁初始化 int ret = pthread_create(&prod,NULL,WorkCancel,(void*)&g_bowl);//该线程拿到锁,然后挂掉 if(ret != 0) { perror("pthread_create"); return -1; } int ret1 = pthread_create(&cons,NULL,WorkWhile,(void*)&ret);//测试该线程是否可以拿到锁 if(ret1 != 0) { perror("pthread_create"); return -1; } pthread_cancel(prod);//取消该线程 pthread_join(prod,NULL);//线程等待 pthread_join(cons,NULL);//线程等待 pthread_mutex_destroy(&mutex);//销毁互斥锁 while(1) { sleep(1); } return 0;}
结果:只要拿到锁,就表明线程清理函数成功了。
