治疗股癣用什么药膏| 什么样的云朵| 吃姜对身体有什么好处| 大保健是什么意思| 皮下囊肿是什么原因引起的| 胰腺在人体起什么作用| 田鸡是什么| 鸡拉白色稀粪吃什么药| 罘是什么意思| 拉不出大便吃什么药| 3月21日什么星座| 身体缺镁会有什么症状| 月什么意思| 卡其色裙子配什么颜色上衣好看| 什么蔬菜含铁高| 胃动力不足吃什么中成药| 911是什么电话| 什么叫随机血糖| 刘备是个什么样的人| 喉咙发炎吃什么药最好| 封神榜讲的是什么故事| 为什么会得阴虱| 小孩睡觉磨牙齿是什么原因| 什么病会引起牙疼| 狗是什么生肖| 世界上最大的海是什么海| 谷草谷丙偏低代表什么| 尿点什么意思| 羊膜是什么| 为什么会突然流鼻血| b族维生素是什么意思| 脾虚生痰吃什么中成药| 中标是什么意思| 急性荨麻疹用什么药| 血小板少是什么病| 宋小宝得了什么病| 一个田一个比念什么| 胆囊炎是什么病| 冠冕堂皇是什么意思| 十月十七是什么星座| 炼乳是什么做的| 死是什么意思| 直销是什么意思| 丝瓜只开花不结果是什么原因| 谨记教诲是什么意思| 手指发麻是什么原因引起的| 孩子脚后跟疼是什么原因| 什么叫子宫肌瘤| 爱是什么结构| 什么是佛跳墙| 鸡眼去医院挂什么科| 毕业答辩是什么| v1是什么意思| 二狗是什么意思| 人到中年为什么会发胖| 掉头发是什么原因女性| 灯火通明是什么生肖| 什么是民间故事| 降火吃什么| 姜子牙是什么神仙| 脚真菌感染用什么药| 分泌物过氧化氢阳性是什么意思| 两个人一个且念什么| 有胆结石的人不能吃什么东西| 8月31号是什么星座| 不偏不倚是什么意思| 什么枯石烂| 金是什么颜色| dollars是什么意思| 手脚发抖是什么原因引起的| 掉头发什么原因| 小龙虾什么季节| 柒牌男装什么档次| 山麻雀吃什么| nova是什么牌子| 画龙点睛是什么意思| 一月28号是什么星座| 桑黄长在什么树上| 猪肝炒什么| 痰培养是检查什么的| 抽筋吃什么药见效快| tg是什么指标| 梦见杀人什么意思| fda是什么| 什么水果泡酒最好| 氯雷他定片主治什么病| 几又念什么| 数字2代表什么意思| 拘禁是什么意思| 牛和什么属相最配| 麻梨疙瘩是什么树| 下饭是什么意思| 抄手是什么食物| cima是什么证书| 用牙膏洗脸有什么好处和坏处| 异次元是什么意思| 安宫牛黄丸什么时候吃| 鸡蛋白过敏指的是什么| 12月22日什么星座| 南明为什么打不过清朝| 右眉毛跳是什么预兆| 明目退翳什么意思| 什么是直径| 九五至尊是什么生肖| 大姨妈不来是什么原因| 过敏性鼻炎喷什么药| 8月31号是什么星座| 破釜沉舟什么意思| 铁皮石斛能治什么病| 中午吃什么饭| 什么什么自如| 空调多少匹什么意思| 治疗股癣用什么药膏| 气血不足挂什么科| 双喜临门的临是什么意思| 左室高电压什么意思| 唐氏筛查都查些什么| 经常放屁是什么问题| 1930年属什么| 什么肉好吃| 胆是起什么作用的| 什么品种荔枝最好吃| 吃桂圆有什么好处| kimi是什么意思| 君子兰的寓意是什么| 脚臭用什么泡脚效果好| 天雨粟鬼夜哭什么意思| 濒死感是什么感觉| 合卺是什么意思| 喝什么减肥| eur是什么意思| 磁共振是做什么的| 什么症状提示月经马上要来了| 身体年龄是什么意思| 蚂蝗吃什么| 什么叫痛风| 内检是什么| 小白和兽神什么关系| 维生素什么时候吃效果最好| 心属于五行属什么| 早上醒来手麻是什么原因| 油条配什么好吃| 百褶裙搭配什么上衣| 喝什么最解渴| 什么是三高| 籍贯一般填什么| 大门是什么生肖| 精忠报国是什么意思| 大姨妈来吃什么好| 九月开什么花| 梅毒螺旋体抗体阳性是什么意思| 人心叵测什么意思| 钟爱一生是什么意思| 什么叫阴吹| 什么肠小道成语| 为什么奢侈品都是pvc| 嘴唇暗红色是什么原因| 二代身份证是什么意思| 医院为什么禁止小孩灌肠| 左脸颊有痣代表什么| 6月1是什么星座| 10.30什么星座| 公婆是什么意思| 得宝松是什么药| 恐龙吃什么| 女性备孕吃什么养卵泡| 劲旅是什么意思| 上山下金是什么字| 吃什么降肝火| 西瓜为什么叫西瓜| 妇科衣原体是什么病| 毒血症是什么病| 衣服的英文是什么| 睡眠不足会引起什么症状| 天降横财什么意思| 不是什么| 缺硒吃什么| 为什么要文化大革命| 月经来了不走是什么原因| 肝内小囊肿是什么意思| shadow是什么意思| 懿字五行属什么| 新生儿便秘怎么办什么方法最有效| 情商什么意思| honor是什么牌子| 阑尾炎手术后可以吃什么| 乙肝第二针最晚什么时候打| 什么化妆品好| 平菇炒什么好吃| 阴干吃什么补雌激素| 二月份出生的是什么星座| 什么冰冰| 发粉是什么| 神经质是什么意思| 第三代身份证什么时候开始办理| 3月27号是什么星座| 为什么一动就满头大汗| 小便无力是什么原因男| 月经来头疼是什么原因引起的| 生完孩子吃什么补身体| 自闭症是什么人投胎| 牛郎是什么职业| 仓鼠可以吃什么蔬菜| 下连是什么意思| 为什么女人阴唇会变大| 柔式按摩是什么意思| 呔是什么意思| 归脾丸和健脾丸有什么区别| 腰肌劳损用什么药最好| 乳腺增生不能吃什么| 减肥为什么不让吃茄子| 为的多音字是什么| 低压高吃什么中成药| 低蛋白血症是什么病| 小肝癌是什么意思| 小孩脱发是什么原因引起的| 为什么会得肠胃炎| 呵呵是什么意思| d3和ad有什么区别| 日记可以写什么| 什么是海藻糖| 玻璃水是什么| 感冒去医院挂什么科| 白凉粉是什么做的| cot什么意思| 十月6号是什么星座| 胃窦是什么意思| 董小姐是什么意思| 什么的松树| 沙棘是什么植物| 吃维生素c有什么好处| 双鱼座是什么星象| 有痰咳嗽吃什么药| 一根葱十分钟什么意思| 艺考音乐考什么| 血瘀是什么原因造成的| 小孩测骨龄挂什么科| 宫颈粘连什么症状| 伏脉常见于什么病| 洗耳恭听什么意思| 肠胃炎拉肚子吃什么药| 散粉和粉饼有什么区别| 不自爱是什么意思| 辜负是什么意思| 为什么一直打嗝| 迷走神经是什么| 家中养什么鸟最干净| 南极有什么| 尿尿泡沫多是什么原因| 米为什么会生虫| 山代表什么生肖| 怎么知道自己缺什么五行| 晕车吃什么好| 五月二十五是什么星座| 舌根痛吃什么药好得快| 梦见大火是什么意思| freeze是什么意思| 阿司匹林肠溶片什么时候吃最好| 百香果是什么季节的| 陆代表什么数字| 汗疱疹是什么| 散文是什么意思| 氨基酸是什么东西| 乳腺结节有什么症状| 百度
"); //-->

博客专栏

EEPW首页 > 博客 > 嵌入式Linux:线程的创建、终止、回收、取消和分离

中国经济信息社陕西中心招聘公告

发布人:美男子玩编程 时间:2025-08-04 来源:工程师 发布文章
百度 小时候学书法,上了几节课后,他感觉很枯燥,索性不去了,父母也由着他。

线程的创建、终止、取消、回收和分离操作是多线程编程的核心。

在多线程编程中,需要妥善管理线程的生命周期,以避免资源泄漏、竞争条件或僵尸线程等问题。

1

创建线程

在 Linux 中,默认情况下,一个进程启动时是单线程运行的,这个线程被称为 主线程。

然而,现代计算任务通常需要并行处理,主线程可以通过 pthread_create() 函数创建额外的线程来并行执行任务。

这些额外的线程与主线程共享进程的资源(如内存空间、文件描述符等),但它们有独立的执行路径。

pthread_create() 函数的定义如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,                   void *(*start_routine) (void *), void *arg);

函数参数:

  • thread:这是一个 pthread_t 类型的指针,指向存储线程 ID 的变量。pthread_t 是用于唯一标识线程的类型,当创建线程成功时,该变量会被赋值为新线程的 ID,在后续的线程管理中使用。

  • attr:这是一个指向 pthread_attr_t 类型的指针,用于设置线程的属性。如果设置为 NULL,则使用线程的默认属性。这些属性包括线程是否分离(detached)、栈大小等。如果需要设置特定的属性,可以使用 pthread_attr_init() 和相关的属性函数。

  • start_routine:这是线程执行函数的指针。新线程创建后,会从这个函数开始执行。该函数必须符合以下原型:

void* (*start_routine)(void *arg);

它接收一个 void* 类型的参数(arg),并返回一个 void* 类型的返回值。

  • arg:这是传递给 start_routine 函数的参数。可以是任意类型的指针。如果不需要传递参数,可以设置为 NULL。如果需要传递多个参数,可以使用结构体将它们打包后通过该指针传入。

返回值:

  • 成功时返回 0。

  • 失败时返回错误号,表示失败的原因。例如,EAGAIN 表示系统资源不足无法创建新线程,EINVAL 表示传入的属性无效。

创建线程的关键点:

  • 线程 ID: 每个线程都有唯一的 ID,用于区分线程。创建线程时,pthread_create() 会将新线程的 ID 存储在 pthread_t 类型的变量中,便于后续操作。

  • 线程属性: 默认情况下,线程使用系统的默认属性。如果需要更改线程的属性,比如将其设置为 分离线程 或指定线程的栈大小,可以通过 pthread_attr_t 来设置。

  • 启动函数和参数: 新线程会从 start_routine 函数开始执行,并传入 arg 参数。可以通过将多个参数封装在结构体中,一并传递给该函数。

当一个新线程被创建后,它立即加入系统的 线程调度队列,并在合适的时机获取 CPU 执行时间。

由于调度是由操作系统控制的,所以无法预料新创建的线程和主线程谁会先执行。

如果程序对线程的执行顺序有严格要求,可以使用同步机制(如 互斥锁 或 信号量)来控制线程间的执行顺序。

下面是一个创建线程并传递参数的简单示例:

void* thread_function(void* arg) {    int* num = (int*)arg;    printf("New thread running with argument: %dn", *num);    return NULL;}
int main() {    pthread_t thread;    int arg = 42;
    // 创建线程,传递参数 arg    if (pthread_create(&thread, NULL, thread_function, &arg) != 0) {        perror("pthread_create failed");        return 1;    }
    // 等待新线程执行完毕    pthread_join(thread, NULL);
    printf("Main thread finishedn");    return 0;}

解释:

  • 线程函数 thread_function() 接收一个 void* 类型的参数,并将其强制转换为 int* 类型,打印传入的值。

  • 主线程 调用 pthread_create() 创建了一个新线程,并将 arg 作为参数传递给新线程的函数 thread_function()。

  • 创建线程后,主线程调用 pthread_join() 等待新线程完成执行。如果不使用 pthread_join(),主线程不会等待新线程结束,这可能导致程序提前退出。

2

终止线程

在 Linux 中,终止线程可以通过多种方式完成,不同的方式影响线程的退出行为和进程的状态管理。

我们详细说明几种终止线程的常用方法。

  • return: 当线程的 start 函数执行 return 时,线程正常终止,并返回指定的值,返回值可以通过 pthread_join() 获取。

  • pthread_exit(): 线程可以通过显式调用 pthread_exit() 来终止自身,pthread_exit() 允许线程在任何位置退出,返回的值也可以通过 pthread_join() 获取。

  • pthread_cancel(): 通过 pthread_cancel() 可以请求取消一个线程,线程需要响应取消请求才能终止。

  • exit() 和 _exit(): 当进程中的任意线程调用 exit()、_exit() 或 _Exit() 时,整个进程,包括所有线程,都会被终止。

2.1、通过 return 语句退出线程

线程的 start 函数(即传递给 pthread_create() 的函数)在执行完毕时,可以直接使用 return 语句返回。这种方式会使线程正常退出,并将返回值作为线程的退出码。

这与调用 pthread_exit() 类似。

void* thread_function(void* arg) {    // 执行一些任务    int result = 42;    return (void*)result; // 通过 return 语句退出线程}

在上面的代码中,线程执行完 thread_function() 后,通过 return 返回 result,并且这个返回值可以通过 pthread_join() 函数在主线程中获取。

2.2、通过 pthread_exit() 退出线程

pthread_exit() 是专门用于退出线程的函数,它允许线程在任何位置显式退出,而不是依赖于 return。

调用 pthread_exit() 后,线程的控制流会立即结束,不再执行后续代码。

pthread_exit() 函数原型:

void pthread_exit(void *retval);

参数 retval: retval 是一个 void* 类型的指针,指定线程的返回值,也就是线程的退出码。这个值可以被其他线程通过 pthread_join() 获取。

示例如下:

void* thread_function(void* arg) {    int* retval = (int*)arg;    printf("Thread exiting with value: %dn", *retval);    pthread_exit(retval); // 显式退出线程并返回值}
int main() {    pthread_t thread;    int arg = 42;    int* retval;
    pthread_create(&thread, NULL, thread_function, &arg);    pthread_join(thread, (void**)&retval); // 获取线程的退出码
    printf("Thread returned: %dn", *retval);    return 0;}

解释:

  • 在该示例中,pthread_exit() 显式终止了线程,并返回参数 arg 的值。

  • 主线程通过 pthread_join() 获取了子线程的退出码。

2.3、通过 exit()、_exit() 或 _Exit() 终止整个进程

exit()、_exit() 和 _Exit() 不是用于终止单个线程的,而是用于终止整个进程。

由于线程共享同一个进程资源,如果任意一个线程调用这些函数,整个进程(包括所有线程)都会终止。

  • exit(): 正常终止进程,执行清理函数、关闭文件描述符等。

  • _exit() 和 _Exit(): 立即终止进程,不执行清理工作。

以下示例中,thread_function() 中调用了 exit(),导致整个进程被终止,主线程也不会继续执行。

void* thread_function(void* arg) {    printf("Thread running...n");    exit(0); // 调用 exit(),导致整个进程终止    return NULL;}
int main() {    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);    pthread_join(thread, NULL);
    // 如果没有被 exit() 终止,主线程会继续执行这行代码    printf("Main thread finishedn");
    return 0;}

3

回收线程

在多线程编程中,当线程结束后,其占用的资源不会立即被系统释放,除非显式回收这些资源,否则这些线程会变成 僵尸线程。

在 Linux 中,回收线程的操作与进程的回收类似。

正如进程中的父进程可以使用 wait() 来回收子进程的资源,线程中也需要通过 pthread_join() 来回收线程资源并获取线程的退出状态。

pthread_join() 是用于 等待指定线程终止并回收其资源 的函数,它会阻塞调用线程直到目标线程终止。

如果线程已经终止,pthread_join() 将立即返回。

通过这个函数,主线程或其他线程可以获取目标线程的退出状态,并清理其占用的资源,避免产生僵尸线程。

pthread_join() 的函数原型:

int pthread_join(pthread_t thread, void **retval);

函数参数说明:

  • thread: 这是目标线程的线程 ID,pthread_join() 将等待这个线程终止。

  • retval: 这是一个指向 void* 类型的指针,指向保存线程返回值的内存地址。如果目标线程通过 pthread_exit() 或 return 语句返回了某个值,这个值将被存储在 *retval 指向的内存中。如果 retval 为 NULL,则表示不关心目标线程的返回值。

返回值:

  • 成功时返回 0。

  • 如果调用失败,pthread_join() 将返回一个错误码。例如,ESRCH 表示指定的线程不存在,EINVAL 表示线程不可被 pthread_join() 回收,或者调用线程尝试等待自身终止。

以下例子中,线程执行完 thread_function() 后通过 pthread_exit() 返回 result。

主线程调用 pthread_join() 等待线程结束,并成功获取到了线程的返回值。

void* thread_function(void* arg) {    int result = 100;    printf("Thread running...n");    pthread_exit((void*)&result); // 显式返回一个结果}
int main() {    pthread_t thread;    int* thread_result;
    // 创建线程    pthread_create(&thread, NULL, thread_function, NULL);
    // 回收线程并获取返回值    pthread_join(thread, (void**)&thread_result);
    printf("Thread returned: %dn", *thread_result);    return 0;}

3.1、pthread_join() 的使用场景与注意事项

pthread_join() 是 阻塞函数,它会一直等待指定线程结束。如果目标线程需要执行大量计算或处理,调用 pthread_join() 的线程将一直处于等待状态,直到目标线程终止。

如果线程已经结束,pthread_join() 将立即返回。

以下示例中,主线程在调用 pthread_join() 时会等待 5 秒,直到 worker_function() 执行完毕为止。

void* worker_function(void* arg) {    sleep(5);  // 模拟一些长时间运行的操作    return NULL;}
int main() {    pthread_t thread;
    pthread_create(&thread, NULL, worker_function, NULL);
    printf("Waiting for thread to finish...n");    pthread_join(thread, NULL); // 阻塞等待线程结束
    printf("Thread finished.n");    return 0;}

在进程中,如果父进程不回收子进程,则子进程会变为 僵尸进程,占用系统资源。

同样的,如果一个线程终止后,没有被其他线程调用 pthread_join() 来回收,其内存和其他资源也不会被立即释放,这就导致了 僵尸线程 的问题。

僵尸线程不仅浪费资源,而且如果僵尸线程累积过多,可能会导致应用程序无法创建新的线程。

3.2、pthread_join() 与进程回收的区别

虽然 pthread_join() 与进程中的 waitpid() 类似,都是用于等待子线程(或子进程)结束并获取其退出状态,但二者之间有一些显著的区别:

1、线程关系是对等的。

在多线程程序中,任何线程都可以调用 pthread_join() 来等待另一个线程的结束。即使是非创建该线程的线程,也可以调用 pthread_join() 来等待它的终止。线程之间没有父子层级关系。

举例来说,如果线程 A 创建了线程 B,线程 B 创建了线程 C,那么线程 A 可以等待线程 C 的结束,而不需要依赖线程 B。

这与进程的父子层级结构不同,父进程是唯一可以调用 wait() 或 waitpid() 来等待子进程终止的进程。

2、pthread_join() 不支持非阻塞等待。

pthread_join() 是阻塞调用,不支持类似waitpid() 中的非阻塞模式(通过传入 WNOHANG 标志实现)。

这意味着线程调用 pthread_join() 后必须等待目标线程终止,不能做其他操作。如果需要更复杂的线程同步,通常需要引入其他机制,如 信号量、条件变量 等。

4

取消线程

通常情况下,线程会自行决定何时结束,比如通过调用 pthread_exit() 函数或者在其启动函数中执行 return 语句。

但有些场景下,主线程或其他线程可能需要 强制终止 某个正在运行的线程,这时就可以通过 取消请求 来实现。

通过调用 pthread_cancel(),可以向目标线程发送一个取消请求,要求它终止。

pthread_cancel() 函数原型如下:

int pthread_cancel(pthread_t thread);

参数说明:

  • thread: 需要取消的目标线程的线程 ID。

返回值:

  • 成功时返回 0。

  • 如果调用失败,返回错误码,例如:ESRCH: 指定的线程不存在。

4.1、线程取消的响应机制

目标线程对取消请求的响应方式可以由其自身决定。每个线程都有一个 取消状态 和 取消类型 来控制它对取消请求的响应:

4.1.1、取消状态

取消状态决定了线程是否允许响应取消请求,线程可以通过调用 pthread_setcancelstate() 来修改其取消状态。

  • PTHREAD_CANCEL_ENABLE: 表示线程 允许 响应取消请求(这是默认状态)。

  • PTHREAD_CANCEL_DISABLE: 表示线程 不允许 响应取消请求。即使收到了取消请求,线程仍会继续运行,直到其取消状态被重新设置为可取消。

pthread_setcancelstate() 函数原型:

int pthread_setcancelstate(int state, int *oldstate);

参数:

  • state: 可以是 PTHREAD_CANCEL_ENABLE 或 PTHREAD_CANCEL_DISABLE,分别表示开启或禁用取消请求的响应。

  • oldstate: 如果不为 NULL,将保存原先的取消状态。

示例如下:

pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); // 禁止取消请求pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);  // 允许取消请求

4.1.2、取消类型

取消类型决定了线程 何时 响应取消请求。

可以通过调用 pthread_setcanceltype() 来设置线程的取消类型:

  • PTHREAD_CANCEL_DEFERRED: 线程将在 某些特定的取消点 响应取消请求(例如调用 pthread_testcancel(),或进行 I/O 操作时)。这是默认的取消类型。

  • PTHREAD_CANCEL_ASYNCHRONOUS: 线程在 收到取消请求的瞬间 就立即响应,可能导致线程在任意位置被取消。

pthread_setcanceltype() 函数原型:

int pthread_setcanceltype(int type, int *oldtype);

参数:

  • type: 可以是 PTHREAD_CANCEL_DEFERRED 或 PTHREAD_CANCEL_ASYNCHRONOUS,分别表示延迟响应取消或立即响应取消。

  • oldtype: 如果不为 NULL,将保存原先的取消类型。

示例:

pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); // 设置为立即响应取消
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);      // 设置为延迟响应取消

4.2、取消点与线程清理

当线程的取消类型设置为 PTHREAD_CANCEL_DEFERRED 时,线程只有在到达某些 取消点 时才会响应取消请求。

取消点通常是一些耗时操作或系统调用,比如:

  • pthread_testcancel(): 显式设置取消点。

  • 其他一些常见的系统调用,比如 I/O 操作、sleep()、select() 等,都是隐式取消点。


系统中还有许多函数可以作为取消点,这里不再逐一列举。您可以通过查看 man 手册获取更多信息,使用命令 man 7 pthreads 进行查询。 

pthread_testcancel() 函数原型:


void pthread_testcancel(void);


这个函数可以在代码的任意位置显式创建一个取消点。


调用 pthread_testcancel() 后,线程会检查是否有取消请求,如果有,线程将在此处退出。


示例如下:


while (1) {    // 执行一些任务    pthread_testcancel(); // 在循环中显式设置取消点,检查是否有取消请求}


4.3、线程清理处理函数

在线程终止时(无论是正常结束还是被取消),可以使用 清理处理函数 来进行资源清理。


清理处理函数可以确保线程在取消时能够正确释放资源,避免资源泄露。


使用 pthread_cleanup_push() 和 pthread_cleanup_pop() 来设置清理函数:

  • pthread_cleanup_push(void (*routine)(void *), void *arg):将一个清理函数 routine 压入栈,当线程退出时,系统将调用该函数。

  • pthread_cleanup_pop(int execute):将清理函数从栈中弹出,execute 表示是否执行该函数。


以下例子中,当线程收到取消请求后,它会在 pthread_testcancel() 函数处响应取消请求并退出。


在线程退出时,cleanup_handler() 会被调用以清理资源。


void cleanup_handler(void *arg) {    printf("Cleanup: %sn", (char *)arg);}
void* thread_function(void* arg) {    pthread_cleanup_push(cleanup_handler, "Thread resources"); // 设置清理函数    while (1) {        printf("Thread running...n");        sleep(1);        pthread_testcancel(); // 检查是否有取消请求    }    pthread_cleanup_pop(1); // 1 表示执行清理函数    return NULL;}
int main() {    pthread_t thread;    pthread_create(&thread, NULL, thread_function, NULL);
    sleep(3);  // 等待几秒钟    pthread_cancel(thread); // 发送取消请求    pthread_join(thread, NULL); // 等待线程结束    printf("Thread has been canceled.n");
    return 0;}


正确处理线程的取消操作对于复杂的多线程应用程序至关重要,特别是在执行长时间任务时,灵活管理线程的取消状态和清理行为能够有效提高系统的稳定性和可靠性。


  • pthread_cancel() 用于向目标线程发送取消请求,要求其终止,但目标线程是否终止取决于其取消状态和取消类型。

  • 线程可以通过 pthread_setcancelstate() 来控制是否响应取消请求,并通过 pthread_setcanceltype() 来控制何时响应。

  • 在使用 延迟取消 的情况下,线程只有在特定的 取消点 处才会检查取消请求,可以通过 pthread_testcancel() 显式设置取消点。

  • 清理处理函数 确保线程在被取消时能够正确释放资源,避免资源泄露。


5

分离线程

默认情况下,线程终止后,其资源不会立即被系统回收,除非有另一个线程通过 pthread_join() 函数显式地等待该线程终止,回收其资源。


但如果某些线程的退出状态和返回值对程序来说并不重要,且不希望手动调用 pthread_join(),可以将该线程设置为 分离状态。


分离状态的线程在终止时,系统会自动回收它的资源。


要将线程设置为分离状态,可以调用 pthread_detach() 函数。


pthread_detach() 函数原型:


int pthread_detach(pthread_t thread);


参数说明:

  • thread: 需要分离的目标线程的线程 ID。


返回值:

  • 成功时返回 0。

  • 如果调用失败,返回错误码,例如:

    • ESRCH: 指定的线程不存在或已经被回收。

    • EINVAL: 线程已经处于分离状态。


调用 pthread_detach() 后,指定的线程会进入分离状态。

处于分离状态的线程在终止时,系统会自动回收其所有资源,而无需其他线程显式调用 pthread_join()。

分离状态是不可逆的,一旦线程被分离,就不能再通过 pthread_join() 获取该线程的返回状态或等待其结束。

以下例子中,创建了一个新线程,并通过 pthread_detach() 将其分离。之后,无需调用 pthread_join(),系统将在该线程终止时自动回收它的资源。


pthread_t thread;pthread_create(&thread, NULL, thread_function, NULL);pthread_detach(thread); // 将该线程设置为分离状态


线程不仅可以由其他线程分离,还可以通过调用 pthread_detach(pthread_self()) 来 分离自己。

这意味着该线程在终止时不需要其他线程来回收资源,系统将自动处理。

示例如下:


void* thread_function(void* arg) {    pthread_detach(pthread_self()); // 分离自己    // 线程执行的其他操作    pthread_exit(NULL);}


线程分离机制特别适用于以下几种场景:

  • 不关心线程的返回值: 如果线程执行的任务不需要返回值,且不希望其他线程显式地等待它结束。

  • 避免僵尸线程: 僵尸线程是指已经终止但资源未被回收的线程,长时间存在僵尸线程会消耗系统资源。将线程设置为分离状态,可以避免僵尸线程的产生。

  • 长时间运行的后台任务: 如果线程运行时间较长或是后台任务,而主线程不需要等待其结束,分离该线程可以简化资源管理。


线程分离与 pthread_join() 的比较:


线程分离:

  • 使用 pthread_detach() 将线程设置为分离状态。

  • 系统在线程终止时自动回收资源。

  • 无法通过 pthread_join() 获取线程的返回值或等待线程终止。


pthread_join():

  • 主动调用 pthread_join() 等待指定线程终止并回收资源。

  • 可以获取线程的返回值或终止状态。

  • 若没有调用 pthread_join(),线程终止后会成为僵尸线程,直到其资源被回收。


线程分离在简化多线程程序的资源管理方面非常有用,特别是对于一些无需等待或回收的线程,可以通过分离机制优化程序的性能和稳定性。

最后讲两点注意事项:

  • 不可逆性: 一旦线程被设置为分离状态,就无法恢复到可被 pthread_join() 回收的状态。如果你将某个线程分离,后续便无法获取其返回值或等待它结束。

  • 线程同步问题: 如果某个线程执行的任务需要与其他线程同步完成,则不应将其分离。否则,主线程或其他线程可能无法等待该线程结束,导致任务未完成就继续执行。


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。



关键词: 嵌入式 Linux

相关推荐

技术专区

关闭
槟榔什么味道 marni是什么品牌 阿司匹林肠溶片有什么副作用 刀厄痣是什么意思 7.3是什么星座
什么叫意象 送什么礼品好 辣眼睛是什么意思 知识渊博是什么意思 胃动力不足吃什么中成药
中性粒细胞偏高是什么原因 食管炎是什么原因引起的 5月有什么节日 蜗牛吃什么 贻笑大方什么意思
微不足道什么意思 太阳花是什么花 生理期可以吃什么 天下乌鸦一般黑是什么意思 妈妈的表姐叫什么
呼吸内镜检查什么zsyouku.com tct检查什么项目hcv9jop1ns0r.cn 吃什么水果能长高hcv7jop5ns1r.cn 什么是脂肪瘤hcv9jop0ns2r.cn 乌克兰和俄罗斯为什么打仗hcv9jop4ns4r.cn
肝不好吃什么药效果好hcv9jop5ns7r.cn 市委副秘书长什么级别hcv9jop7ns5r.cn 肾不好会有什么症状hcv9jop1ns5r.cn 酸笋炒什么好吃hcv8jop9ns5r.cn 资生堂适合什么年龄段hcv7jop5ns0r.cn
文爱什么意思hcv8jop4ns9r.cn 西米是用什么做的hcv8jop2ns8r.cn 张什么舞什么hcv9jop7ns2r.cn 复方甘草酸苷片治什么病hcv9jop5ns1r.cn 为什么胸会痒hcv7jop7ns1r.cn
睾丸痛挂什么科hcv7jop7ns2r.cn 槐花什么时候开花hcv9jop3ns1r.cn 无菌性前列腺炎吃什么药效果好hcv8jop6ns3r.cn 肉桂粉是什么做的hcv9jop5ns6r.cn 女人人中深代表什么zsyouku.com
百度