1. 线程取消概述
线程取消(Thread Cancellation)是 POSIX 线程(pthread)提供的一种机制,允许一个线程请求终止另一个线程的执行。这类似于进程间的信号机制,但专用于线程间控制。
核心思想:被取消的线程并非立即被强制杀死,而是会在一个安全的"取消点"(Cancellation Point)响应取消请求,进行资源清理后退出。这种设计避免了资源泄漏和数据不一致。
2. 取消相关 API 函数
2.1 发起取消请求:pthread_cancel
c
int pthread_cancel(pthread_t thread);
- 功能:向指定线程发送取消请求。
- 参数 :
thread为目标线程的标识符。 - 返回值:成功返回 0,失败返回错误码。
- 说明:调用此函数仅表示发出了取消请求,目标线程是否以及何时终止,取决于其取消状态和取消类型。
2.2 设置取消状态:pthread_setcancelstate
c
int pthread_setcancelstate(int state, int *oldstate);
- 功能:设置本线程的取消功能为"启用"或"禁用"。
- 参数 :
state:新状态,取值为:PTHREAD_CANCEL_ENABLE:启用取消(默认值)。PTHREAD_CANCEL_DISABLE:禁用取消。在此状态下,取消请求会被挂起,直到状态重新变为ENABLE。
oldstate:用于返回旧状态的指针(可为NULL)。
- 返回值:成功返回 0,失败返回错误码。
2.3 设置取消类型:pthread_setcanceltype
c
int pthread_setcanceltype(int type, int *oldtype);
- 功能:设置本线程响应取消请求的时机(类型)。
- 参数 :
type:新类型,取值为:PTHREAD_CANCEL_DEFERRED:延迟取消(默认值)。线程只在抵达"取消点"时才响应取消请求。PTHREAD_CANCEL_ASYNCHRONOUS:异步取消。线程可能在任意指令点被立即取消(危险,极少使用)。
oldtype:用于返回旧类型的指针(可为NULL)。
- 返回值:成功返回 0,失败返回错误码。
2.4 手动建立取消点:pthread_testcancel
c
void pthread_testcancel(void);
- 功能:如果本线程有挂起的取消请求,则立即在此处响应取消(线程终止)。如果没有挂起的请求,则此函数什么都不做。
- 说明:当线程长时间运行且不经过系统调用等天然取消点时,可以插入此函数来手动创建取消点,使线程能够及时响应取消。
3. 核心概念:取消点(Cancellation Point)
3.1 什么是取消点?
取消点是线程检查是否有挂起取消请求的位置。只有在取消状态为 ENABLE 且取消类型为 DEFERRED(默认情况)时,线程才会在取消点响应取消。
3.2 常见的取消点
POSIX 标准定义了许多函数为取消点,主要包括:
- 阻塞的系统调用 :如
read,write,accept,sleep,pthread_cond_wait。 - 特定的库函数 :如
printf,fopen,sem_wait。 - 手动取消点 :
pthread_testcancel()。
3.3 为什么需要取消点?
直接在任何位置杀死线程是危险的,可能导致:
- 持有锁未释放(死锁)。
- 动态分配的内存未释放(内存泄漏)。
- 文件描述符未关闭。
- 数据结构处于不一致状态。
取消点机制确保了取消只发生在函数调用边界,此时系统或库函数有机会进行清理工作。
4. 使用流程与示例
4.1 基本使用流程
- 发起方线程 :调用
pthread_cancel(target_thread)。 - 目标线程 :
- 默认情况下(
ENABLE+DEFERRED),继续运行直到下一个取消点。 - 在取消点,线程开始取消序列:执行已注册的清理函数,然后线程终止。
- 默认情况下(
4.2 示例代码:可取消的 worker 线程
c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* worker_thread(void *arg) {
// 线程主循环
while (1) {
// 模拟工作
printf("Working...\n");
sleep(1); // sleep 是一个取消点
// 如果长时间没有系统调用,可以手动插入取消点
pthread_testcancel();
}
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, worker_thread, NULL);
sleep(3); // 让 worker 运行 3 秒
// 取消 worker 线程
printf("主线程请求取消 worker 线程\n");
pthread_cancel(tid);
// 等待线程结束
pthread_join(tid, NULL);
printf("worker 线程已结束\n");
return 0;
}