• 【Linux】线程(二:线程控制)


    本篇文章主要围绕线程控制来进行展开。
    主题思路是以create与join两个接口展开。

    pthread_create 与 pthread_join:

    现在只是粗略的了解一下create与join这个函数的参数与返回值:

    pthread_create:

    这是进行线程创建的函数
    在这里插入图片描述
    参数一:
    他是一个输出型参数,为线程的id,这是库提供的类型,本是一个无符号长整型在这里插入图片描述
    参数二:
    这个是关于线程属性的,不会用到,我们写为nullptr即可。
    参数三:
    是一个函数指针,是我们新线程要去执行的函数。
    参数四:
    传参给参数三中的函数。

    关于返回值,带有pthread前缀的函数返回值都是统一的:
    成功时返回0,失败时返回错误码。

    pthread_join:

    我们的进程需要等待,线程当然也需要进行等待,join就是。
    在这里插入图片描述
    参数一:
    将指定tid传给他即可。
    参数二:
    这是接收新线程执行函数的返回值,我们暂时不关心,先设置为nuliptr。

    随后会围绕这两个函数进行展开,细节都会涉及到。

    代码:

    进程控制本质上就是一堆的函数调用,我们当然是要结合代码来看。

    问题一:主线程与新线程谁先退出?

    我们先写一段最简单的代码:
    在这里插入图片描述
    观察现象:
    在这里插入图片描述
    那么这里后出现一个问题,也就是我们的问题一。
    在这里插入图片描述
    所以应该谁先运行,答案是不确定的,
    因为它取决于OS的调度策略与运气等因素,好吧…

    问题二:哪个线程应该最后退出?

    与我们的进程一样,进程需要父进程来进行等待,所以父进程应最后退出;
    线程也应该是主线程最后退出,进行对应的等待。

    那么怎么保证他可以最后退出?答案就是join会进行阻塞等待。

    如果主线程不进行join?
    当主线程退了,那么整个进程就相当于退出了。我们不推荐这种做法,这样的行为是没有意义的。
    当主线程没退,新线程跑完,会出现类似僵尸进程的概念。

    对于join可以进行等待我们可以验证一下:

    在这里插入图片描述
    对threadRun进行修改一下即可。
    利用命令:while ;: do ps -aL ; sleep 1; done进行观察,果然过了5s后都退出了。
    在这里插入图片描述

    问题三:tid是什么样子的?

    我们已经说过他的本质是一个无符号数字,我们来看看:
    他是什么样子。

    在这里插入图片描述
    打印出来是个很大的数字?
    在这里插入图片描述
    为什么和我们的LWP不一样?
    tid实际上是一个虚拟地址,更具体一些的需要等等再说。

    问题四:怎么更好的理解新线程函数的传参?

    我们已经实验过使用对于传参我们可以传一个字符串,进行强转就可以使用,那么我们可以穿内置类型指针,甚至自定义类型指针?

    我们一步一步来看,先来看内置类型的。

    我们先在栈中定义一个变量a,将他的地址强转为void*传给函数。
    在这里插入图片描述

    现象:果然循环5次打印10。
    在这里插入图片描述
    那么自定义类型?

    代码:
    在这里插入图片描述

    在这里插入图片描述
    对于ThreadRun函数修改一下打印即可。
    在这里插入图片描述
    现象:
    在这里插入图片描述
    这就意味着我们可以给线程传递多个参数甚至是方法。

    但是我们这样写的代码还有一个问题,因为我们创建的data对象在主线程栈区中,这样做会有两个后果:

    1. 破坏了主线程的完整性
    2. 若存在多线程,每一个都会这个变量做修改,会影响别的进程。

    所以我们推荐在堆上开辟空间。
    在这里插入图片描述

    问题五:怎么更好的理解新线程函数的返回值?

    与我们的问题四一样,也可以传递各种各样的数据地址。

    但是我们现在要了解一下join的返回值,因为我们在上边一直说现在我们不关心,设置为nullptr即可。

    在这里插入图片描述

    在这里插入图片描述
    但是为什么传入的是一个void**的二级指针呢?
    因为我们想得到返回值是void类型,如果传递给他一个void的值,因为形参是实参的临时拷贝,所以传递void*并不会得到你想要的值,只能传递他的地址。

    含义与下段代码的是一致的,如果进行传值返回是得不到对应的期望值的。在这里插入图片描述

    我们让新线程函数返回一个整数来感受一下。

    局部修改后的代码:返回一个void*
    在这里插入图片描述
    使用long long接收一下即可,使用int会报err,因为int是4字节,而64
    位下地址为8字节。
    在这里插入图片描述
    现象:果然拿到了111
    在这里插入图片描述
    其实这个也可以当做退出码来理解。

    对于进程来说,一个进程退出有3中状态

    1. 代码跑完,结果不对
    2. 代码跑完,结果对
    3. 出异常。

    这个线程的返回值可以做到前两点,那么出异常呢?
    线程是进程的一部分,线程收到异常进程就会收到异常,理所当然的进程就结束掉了。

    验证:
    让新线程出现野指针的错误,同时对主线程休眠100s,观察是否会直接终止。
    在这里插入图片描述
    现象:

    在这里插入图片描述
    所以我们也侧面验证了多线程的健壮性比较差。

    问题六:如何创建多线程?

    因为多个线程创建好创建但是回收有点麻烦,因此我们可以搞一个vector容器将tid进行管理,这样join时也容易join。

    非常的简单哦,注意不要讲主线程栈上的地址当做参数传给新线程!
    如果在循环中这样做的话会导致两个问题

    1. 对象不断被覆盖(若是编译器优化时不重新分配地址,直接用上一个;若是重新分配则上一个实际上就已经被销毁了,你只是在非法访问!)。
    2. 出了for循环对象销毁。

    代码:
    在这里插入图片描述
    现象:在这里插入图片描述

    问题七:线程如何终止?

    前六个问题已经将创建与等待部分的内容都涉及了,接下来就是新线程的终止等问题。

    方法1: return
    我们终止新线程时一直使用的都是此方法,就不在进行赘述了。

    那我们可以进行exit吗?
    不可以,这个是用来终止进程的!

    方法二pthread_exit
    虽然不允许exit,但是线程库提供了pthread_exit。
    在这里插入图片描述
    这个函数使用方法与return完全一致,将其中的参数填为你想return的值即可。

    方法三:pthread_cancel
    在这里插入图片描述

    这个一般用主线程进行取消。

    关于取消后的新线程返回值,为-1。

    代码:
    在这里插入图片描述
    dfa87c76139ec6f6c1.png)

    现象:
    在这里插入图片描述

    注意:我们进行取消时一定要保证线程先存在!若是create后直接cancel因为竞态条件等因素的存在可能会导致先cancel再create。

    问题九:可以不进行join吗?

    不想等待子进程时我们直接对子进程进行捕捉进而忽略即可,那么现成有这种方法吗?

    答案是有的!
    线程分离。
    在这里插入图片描述

    a. 一个线程被创建默认是joinable的,必须被join
    b. 如果一个线程被分离,那么他的工作状态为分离状态,不需要也不能被join。

    我们举一个例子来进行理解:

    一个家庭里有一非常叛逆的儿子,与家人争吵了,虽然还在一个屋檐下,但是不管那个孩子了。

    分离的线程就是那个儿子,他不需要被等待了,这就叫分离。

    我们有两种分离,比如你找你的父亲吵架,或者你的父亲找你吵架,对应着新线程分离与主线程分离。

    我们分别来看一看~
    新线程主动分离:

    我们的线程有一个获取自己LWP的方法:pthread_self,与getpid一样。
    在这里插入图片描述
    返回值:
    在这里插入图片描述

    被分离的进程进行join会直接返回,不在阻塞了!

    同样,主线程也是同理。

    本节完~

  • 相关阅读:
    Java 格式化时间与时间戳与时间间隔
    app逆向(8)|app的加固+脱壳和frida+rpc介绍
    gdb-dashboard的简单使用
    【LeetCode】二叉树相关题解汇总
    Letbook Cookbook题单——数组2
    天振股份上市首日破发:市值蒸发约8亿元,方庆华夫妇为实控人
    谈谈前端和后端的选择
    Ansible
    mybatis实战:四、insert 用法(普通插入、返回主键自增的值)
    2023CSP-J游寄
  • 原文地址:https://blog.csdn.net/2301_78636079/article/details/139752967