线程的清理
线程的清理pthread_cleanup_push 与 pthread_cleanup_pop 详解在多线程编程中资源清理是一个重要但容易被忽视的问题。当线程异常终止时如何确保已分配的资源如内存、文件句柄、锁等被正确释放POSIX 线程库提供了pthread_cleanup_push和pthread_cleanup_pop这一对函数用于注册线程退出时的清理回调。本文将详细介绍这对函数的用法、执行条件和注意事项。函数原型voidpthread_cleanup_push(void(*routine)(void*),void*arg);voidpthread_cleanup_pop(intexecute);pthread_cleanup_push: 将一个清理函数routine压入当前线程的清理栈。arg是传递给routine的参数。pthread_cleanup_pop: 从清理栈中弹出一个清理函数。如果execute参数非零则执行该清理函数否则仅将其弹出栈。routine 函数被执行的条件清理函数routine会在以下三种情况下被执行线程被pthread_cancel取消当其他线程调用pthread_cancel取消本线程时所有已注册的清理函数会按注册顺序的逆序执行。线程调用pthread_exit退出显式调用pthread_exit也会触发清理函数的执行。以非零参数调用pthread_cleanup_pop当pthread_cleanup_pop(1)被调用时会执行栈顶的清理函数。重要如果线程通过return从线程函数正常返回不会触发清理函数的执行。基本用法示例#includepthread.h#includestdio.h#includestdlib.hvoidcleanup_handler(void*arg){printf(清理资源: %s\n,(char*)arg);// 这里可以释放内存、关闭文件、解锁等操作}void*thread_func(void*arg){// 注册清理函数pthread_cleanup_push(cleanup_handler,资源1);// 模拟一些工作printf(线程正在工作...\n);// 这里如果发生取消或调用 pthread_exitcleanup_handler 会被执行// 弹出清理函数并执行参数为1pthread_cleanup_pop(1);returnNULL;}intmain(){pthread_ttid;pthread_create(tid,NULL,thread_func,NULL);pthread_join(tid,NULL);return0;}注意事项必须成对使用pthread_cleanup_push和pthread_cleanup_pop必须严格成对出现且必须在同一词法作用域内。即使pthread_cleanup_pop不会被执行到如在条件分支中也必须写上否则编译会报错。执行条件只有当pthread_cleanup_pop的参数非零时对应的清理函数才会被执行。如果参数为0则仅从栈中弹出而不执行。支持多对嵌套可以多次调用pthread_cleanup_push注册多个清理函数它们会被压入一个栈中。执行顺序是后进先出LIFO即最后注册的函数最先执行。return 不触发清理线程通过return语句正常返回时不会执行任何清理函数。这是与pthread_exit的重要区别。实现机制这两个函数通常通过宏实现可能展开为包含{和}的代码块。因此它们必须出现在同一函数内且不能跨越多个语句块。实际应用场景场景1动态内存清理void*thread_malloc(void*arg){char*buffermalloc(1024);if(!buffer)returnNULL;pthread_cleanup_push(free,buffer);// 使用 buffer...// 如果线程被取消free(buffer) 会被自动调用pthread_cleanup_pop(1);// 执行 free(buffer)returnNULL;}场景2文件描述符关闭voidcleanup_file(void*arg){FILE*fp(FILE*)arg;if(fp)fclose(fp);printf(文件已关闭\n);}void*thread_file(void*arg){FILE*fpfopen(data.txt,r);if(!fp)returnNULL;pthread_cleanup_push(cleanup_file,fp);// 读取文件...// 如果发生错误可以调用 pthread_exit 确保文件被关闭pthread_cleanup_pop(1);// 正常执行时关闭文件returnNULL;}