【Linux多线程】线程的终止、等待和分离

文章目录

  • 线程终止
    • 正常退出
      • return 退出
      • pthread_exit函数终止线程
    • pthread_cancel强制终止线程
    • 进程终止
  • 线程等待
    • 为什么需要等待线程?
    • pthread_join函数
  • 分离线程
    • pthread_detach函数

线程终止

下面给出终止线程的三种方式:

  1. 正常退出
    • 线程执行完它的函数之后return自动结束
    • 线程显示调用pthread_exit函数退出
  2. 强制终止
    • 一个线程可以被另一个线程通过pthread_cancel函数强制退出
  3. 进程终止
    • 一个进程终止,那么该进程的所有线程都会终止

下面将分别模拟这三种终止线程的方式:

正常退出

return 退出

给出下面代码模拟线程return终止

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    while (cnt--)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
    }
    cout << "phread end....." << endl;
    sleep(1);
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    pthread_join(tid, NULL); // 等待线程结束
    sleep(20);
    cout << "wait success! main thread end!" << endl;
    return 0;
}

在这里插入图片描述
观察上面代码运行情况,发现当线程return之后确实被终止了(上图多出来一个线程是库里面的管理线程,不必理会)。

pthread_exit函数终止线程

pthread-exit函数用于终止当前线程
函数原型:

void pthread_exit(void *value_ptr);
  • value_ptr是一个任意类型的指针,表示线程的退出状态。其它线程可以通过pthread_join函数获取这个状态

给出下面代码观察线程终止

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    while (true)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
        if (cnt == 0)
        {
            cout << "phread exit....." << endl;
            pthread_exit((void *)(status));
        }
        cnt--;
    }
    // cout << "phread end....." << endl;
    // sleep(1);
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;
    pthread_join(tid, &status); // 等待线程结束
    sleep(5);
    cout << "wait success! main thread end! status: " << *(int *)(status) << endl;
    return 0;
}

在这里插入图片描述
需要注意,pthread_exit或者return返回的指针指向的内存单元应该是全局的,因为线程终止之后其函数栈帧会销毁,之后才会返回一个void*指针。

pthread_cancel强制终止线程

功能:取消一个执行中的线程
函数原型:

int pthread_cancel(pthread_t thread);
  • thread表示要删除的线程id(用户级)
  • 成功返回0,否则返回错误码

给出代码样例,观察pthread_cancel函数的使用

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    while (true)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
        // if (cnt == 0)
        // {
        //     cout << "phread exit....." << endl;
        //     pthread_exit((void *)(status));
        // }
        // cnt--;
    }
    // cout << "phread end....." << endl;
    // sleep(1);
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;

    sleep(5);
    pthread_cancel(tid); // 强制终止tid
    cout << "thread is end" << endl;
    pthread_join(tid, &status); // 等待线程结束
    sleep(3);
    cout << "wait success! main thread end! status: " << *(int *)(status) << endl;
    return 0;
}

在这里插入图片描述
在主线程中调用pthread_cancel函数强制终止了执行rout函数的线程。

进程终止

进程终止,该进程的所有线程都终止。比如在函数中调用exit终止进程。

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    while (true)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
        if (cnt == 0)
        {
            cout << "phread exit....." << endl;
            exit(0);
        }
        cnt--;
    }
    return NULL;
}

一旦调用exit或者异常而终止进程,该进程的所有线程都玩完。道理很简单,这里就不做演示了。

线程等待

为什么需要等待线程?

其实已经退出的线程并没有完全“结束”,其栈帧并没有随着线程终止马上就释放,仍然在进程的地址空间里。并且,如果不对这些已经终止但是还没有被释放空间的线程做处理,往后继续创建新线程都不会复用前面退出线程的地址空间,这就造成了资源的浪费。这种情况其实非常像我们之前谈过的僵尸进程问题。

等待线程终止就是提醒内核可以释放这个线程的资源了。等待线程终止其实也是为了确保线程完成任务。有时主线程或者其他线程需要等待某一个线程任务完成之后才能继续执行。等待一个线程结束,其实就是让终止的线程退出时“通知”一下其它线程,可以不关心退出线程的返回结果。此外,等待线程终止在某些情况下能保证数据的完整性,比如线程一处理上半段数据,线程二处理下半段数据,如果其中任何一个线程没有终止,那总数据就会不完整。

为了实现线程终止时的等待问题,linux提供了pthread_join函数。

pthread_join函数

作用:阻塞调用该函数线程,直到目标线程终止。
函数原型:

#include<pthread.h>
int pthread_join(pthread_t thread,void** retval);

在这里插入图片描述
其中:

  • thread表示所等待线程的线程标识符
  • retval是一个输出型参数,指向等待线程的退出信息
  • 成功返回0,否则返回错误码

值得注意的是,线程终止方式的不同,其通过pthread_join得到的退出信息也就不同。比如:

  1. 目标线程通过return 终止(或者是pthread_exit),retval所指向的单元里存放的就是目标线程执行函数的返回值。
    观察下面代码,分析线程return终止时,pthread_join得到的返回值
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    int num = 20;
    while (cnt--)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
    }
    // pthread_exit((void *)status);
    return (void *)status;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;
    cout << "main pthread wait...." << endl;
    pthread_join(tid, &status); // 等待线程结束
    sleep(3);
    cout << "wait success! main thread end! status: " << *(int *)(status) << endl;
    return 0;
}

在这里插入图片描述

  1. 如果线程是被别的线程通过调用pthread_cancel函数强制终止掉,retval所指向单元存放的就是常数PTHREAD_ CANCELED

给出代码样例观察结果:

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    int num = 20;
    while (cnt--)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
    }
    pthread_exit((void *)status);
    // return (void *)status;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;
    sleep(2);
    pthread_cancel(tid);
    pthread_join(tid, &status); // 等待线程结束
    if (status == PTHREAD_CANCELED)
    {
        cout << "pthread is cancel" << endl;
    }
    return 0;
}

在这里插入图片描述
retval所指向单元存放的就是常数PTHREAD_ CANCELED得证。

  1. 如果不关心某一个线程的退出信息,可以return NULL。比如:
void* thread_function(void* arg) {
    // 线程的工作
    printf("Thread is running\n");
    return NULL;
}

总结:

分离线程

分离线程实际上是线程的一种状态,这种状态表示该进程不需要被等待,且线程退出后会自动释放资源。对于主线程来说,有些线程独立执行任务,其它线程没有必要再调用pthread_join阻塞等待。这样提升了整体的效率。一般来说,创建的新线程默认都是joinable的,也就是需要被等待的,我们可以通过pthread_detach函数来改变这一性质。

pthread_detach函数

功能:分离一个目标线程,使该线程终止后自动释放资源,不需要被等待
函数原型:

#include<pthread.h>
int pthread_detach(pthread_t thread);
  • thread表示分离的目标线程,也可以是调用该函数的线程本身
  • 成功放回0,否则-1

给出代码样例,观察pthread_detach函数的使用

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
using namespace std;

void *thread_run(void *arg)
{
    pthread_detach(pthread_self());
    printf("%s\n", (char *)arg);
    return NULL;
}

int main(void)
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, thread_run, (void *)"thread1 run...") != 0)
    {
        printf("create thread error\n");
        return 1;
    }

    int ret = 0;
    sleep(1); // 很重要,要让线程先分离,再等待

    if (pthread_join(tid, NULL) == 0)
    {
        printf("pthread wait success\n");
        ret = 0;
    }
    else
    {
        printf("pthread wait failed\n");
        ret = 1;
    }
    return ret;
}

在这里插入图片描述
分析上述代码,因为执行thread_run函数的线程设置成了分离状态,函数结束之后自动释放资源。此时再去用pthread_join函数去等待这个线程就会得到返回值0。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/696003.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小冬瓜AIGC 手撕LLM 拼课

小冬瓜aigc手撕LLM学习 官方认证 手撕LLMRLHF速成班-(附赠LLM加速分布式训练超长文档&#xff09; 帮助多名同学上岸LLM方向&#xff0c;包括高校副教授&#xff0c;北美PhD&#xff0c;大厂等 课程名称【手撕LLMRLHF】 授课形式&#xff1a;在线会议直播讲解课后录播 时间&…

祝大家端午节安康

五月到端午&#xff0c;愿你端来快乐&#xff0c;无烦无恼&#xff1b;端来好运&#xff0c;无时无刻&#xff1b;端来健康&#xff0c;无忧无虑&#xff1b;端来财富&#xff0c;五谷丰登&#xff1b;端来祝福&#xff0c;五彩缤纷。端午节安康&#xff01;

记录一次被谷歌封号后又解封的过程

先提前恭祝2024年所有参加高考的学子们都能金榜题名&#xff0c;会的全对&#xff0c;不会的蒙的全对&#xff01; 一、背景 众所周知&#xff0c;谷歌、ios应用市场对app的审查都是极其严格的&#xff0c;开发者稍有不慎就会被谷歌下架应用&#xff0c;乃至封号。我们公司是做…

Linux:冯·诺依曼体系结构和操作系统

文章目录 冯诺依曼体系结构操作系统概念操作系统的作用定位机制操作系统如何管理硬件 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识的计算机&…

【Python】在【数据挖掘】与【机器学习】中的应用:从基础到【AI大模型】

目录 &#x1f497;一、Python在数据挖掘中的应用&#x1f495; &#x1f496;1.1 数据预处理&#x1f49e; &#x1f496;1.2 特征工程&#x1f495; &#x1f497;二、Python在机器学习中的应用&#x1f495; &#x1f496;2.1 监督学习&#x1f49e; &#x1f496;2.2…

高考分数查询结果自动推送至微信(卷II)

祝各位端午节安康&#xff01;只要心中无结&#xff0c;每天都是节&#xff0c;开心最重要&#xff01; 在上一篇文章高考分数查询结果自动推送至微信&#xff08;卷Ⅰ&#xff09;-CSDN博客中谈了思路&#xff0c;今天具体实现。文中将敏感信息已做处理&#xff0c;读者根据自…

6.全开源源码---小红书卡片-跳转微信-自动回复跳转卡片-商品卡片-发私信-发群聊-安全导流不封号-企业号白号都可以用

现在用我们的方法&#xff0c;可以规避违规风险&#xff0c;又可以丝滑引流&#xff0c;因为会以笔记的形式发给客户&#xff0c;点击之后直接跳微信&#xff0c;我们来看看演示效果吧&#xff08;没有风险提示&#xff09; 无论是引流还是销售产品都会事半功倍。

redis03 补充 redis驱动模型:事件驱动

1.文件事件 1.1 1.2 注&#xff1a; epoll是linux系统的底层IO多路复用技术 kqueue是mac的底层IO多路复用技术 在 Epoll 中&#xff0c;Epoll 就是事件通知器&#xff0c;可以向 Epoll 注册我们感兴趣的事件。 1.3 1.4 5. 5.1 5.2 5.35.4

onesixtyone一键扫描SNMP服务(KALI工具系列二十)

目录 1、KALI LINUX 简介 2、onesixtyone工具简介 3、在KALI中使用onesixtyone 3.1 目标主机IP&#xff08;win&#xff09; 3.2 KALI的IP 4、操作示例 4.1 扫描目标主机 4.2 加上团队名称 4.3 输出详细结果 4.4 扫描整个网段 5、总结 1、KALI LINUX 简介 Kali Lin…

ThinkPHP发邮件配置教程?群发功能安全吗?

ThinkPHP发邮件的注意事项&#xff1f;如何优化邮件发送的性能&#xff1f; 无论是用户注册、密码重置还是消息提醒&#xff0c;发送邮件都是一个常见的需求。AokSend将详细介绍如何在ThinkPHP框架中配置和发送邮件&#xff0c;帮助开发者轻松实现邮件功能。 ThinkPHP发邮件&…

Discuz! X3.4发帖时间修改插件批量操作版

下载地址&#xff1a;Discuz! X3.4发帖时间修改插件批量操作版 发帖时间与回复时间说明 1、使用本插件修改发帖时间&#xff0c;则帖子中的回复楼层的时间会保持同步同间隔修改&#xff0c;所谓同步同间隔就是如果某个回复是在主题发布之后一小时回复的&#xff0c;那么修改之…

MySQL—多表查询—练习(1)

一、引言 上几篇关于多表查询的基本几个部分全部学习完了。 多表查询的基本类型的查询包括以下&#xff1a; 1、内连接&#xff08;隐式内连接、显示内连接&#xff09;&#xff1a;... [INNER] JOIN ... ON 条件; &#xff09; 2、外连接&#xff08;左外连接、右外连接&…

[沉迷理论]进制链表树

往期文章推荐&#xff1a; 题解之最大子矩阵-CSDN博客 洛谷P1115最大子段和[神奇的题目]-CSDN博客 &#xff08;一条神奇的分割线&#xff09; 前言 好久没有更新的我总算在百忙之中抽出时间写了篇博客。 最近总算结束了动态规划的学习&#xff0c;真的是头昏脑涨啊。 最…

MySQl基础----Linux下搭建mysql软件及登录和基本使用(附实操图超简单一看就会)

绪论​ 涓滴之水可磨损大石&#xff0c;不是由于他力量强大&#xff0c;而是由于昼夜不舍地滴坠。 只有勤奋不懈地努力&#xff0c;才能够获得那些技巧。 ——贝多芬。新开MySQL篇章&#xff0c;本章非常基础包括如何在Linux上搭建&#xff08;当然上面的SQL语句你在其他能执行…

初阶 《分支和循环语句》 3.循环语句

3.循环语句 while for do while 3.1 while循环 前面已经掌握了 if 语句&#xff1a; if(条件)   语句; 当条件满足的情况下&#xff0c;if语句后的语句执行&#xff0c;否则不执行&#xff1b;但是这个语句只会执行一次。 由于我们发现生活中很多的实际的例子是&#xff1a;同…

MYSQL 索引下推 45讲

刘老师群里,看到一位小友 问<MYSQL 45讲>林晓斌的回答 大意是一个组合索引 (a,b,c) 条件 a > 5 and a <10 and b123, 这样的情况下是如何? 林老师给的回答是 A>5 ,然后下推B123 小友 问 "为什么不是先 进行范围查询,然后在索引下推 b123?" 然后就…

Leetcode 力扣114. 二叉树展开为链表 (抖音号:708231408)

给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同。 示例 1&#xf…

欢乐打地鼠小游戏html源码

这是一款简单的js欢乐打地鼠游戏&#xff0c;挺好玩的&#xff0c;老鼠出来用鼠标点击锤它&#xff0c;击中老鼠获得一积分。 欢乐打地鼠小游戏html源码

不同数据库背后的数据存储方案

在大数据和AI时代&#xff0c;数据库成为各类应用不可或缺的重要组成部分。而数据库中的数据依赖存储引擎进行管理&#xff0c;包括数据的存储、查询、更新和删除等。因此&#xff0c;在设计系统时&#xff0c;选择正确的数据库存储引擎方案变得尤为重要。这篇文章将以关系型、…

滑动窗口算法:巧妙玩转数据的窗外世界

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 滑动窗口是什么&#xff1f; 二 相关题目解析 1. 长度最小的子数组 &#x1f973;题目解析 &#x1f973;算法原理 ✏️思路1 暴力枚举出所有子数组之和 ✏️思路2 滑动窗…