线程的控制

在这里插入图片描述

1. Linux线程创建和等待

在这里插入图片描述
这是前面写过的创建线程的代码,现在我们把线程的ID打印看一下:
在这里插入图片描述
我们可以看到线程的id特别大,这是为什么呢?我们后面再说。

如果我们想自己获取自己的线程ID,该怎么办呢
在这里插入图片描述
那么谁调用了这个函数,就获取谁的线程ID。

在这里插入图片描述
这里我们可以先写个打印函数,哪个线程对应的线程id我们打印出来。
在这里插入图片描述
运行结果如下:
在这里插入图片描述
可以看到每个线程的id不相同。

那么创建一个线程后我们也必须等待,不然可能会造成内存泄漏:
在这里插入图片描述

如果线程异常的该怎么办呢
在这里插入图片描述
我们在这个线程5秒之后出现异常,来看运行情况:
在这里插入图片描述
线程异常会导致整个进程异常,线程会影响到其它线程,它的健壮性降低。

pthread_join的第二个参数value_ptr的意义是什么
它是一个输出型参数,获取新线程退出时的退出码
1. 线程退出的方式,return
在这里插入图片描述因为返回值是void*的,所以我们需要先强转。
在这里插入图片描述
在打印ret,我们也需要强转成long long,不然会出现段错误。

运行结果如下:
在这里插入图片描述
从运行结果可以看出:可以成功获取线程的退出码。

我们知道:进程退出有3种方式,1.代码跑完,结果正确。2.代码跑完,结果不正确。3.异常退出。

但是线程退出不需要异常,因为线程异常等于进程异常。以后我们只考虑线程正常终止

2. 线程退出的方式,pthread_exit
在这里插入图片描述
在这里插入图片描述
3. 线程退出的方式,pthread_cancel
在这里插入图片描述
这里是获取线程的id来退出。

在这里插入图片描述
首先,我们让新线程死循环。
在这里插入图片描述
让主线程去取消新线程。
在这里插入图片描述
从运行结果我们可以看到:新线程也成功退出了。

为什么结果是-1呢
因为线程退出,是OS帮我们做的。它会帮我们在线程的task_struct里面修改对应的退出码

4. 线程退出的方式,以new的方式
在这里插入图片描述
我们把new出来的地址返回。
在这里插入图片描述
我们也可以在外面销毁空间。

运行结果如下:
在这里插入图片描述

2. 线程ID

从上面的结果我们可以看出线程的ID特别大,那么线程的ID到底是什么呢
它其实就是一个地址!我们知道:线程是一个独立的执行流,并且线程一定会在自己的运行过程中,产生临时数据(调用函数,定义局部变量等)

线程一定需要有自己的独立的栈结构
下面就谈一谈线程栈:
在这里插入图片描述
这里我们使用的线程库是用户级线程库,它是动态链接。

它的链接过程如下:
在这里插入图片描述
首先,我们先把自己的线程加载到物理内存,然后通过页表映射到代码区去执行。当我们执行到pthread_creat这种库函数时,发现没有,就会从磁盘中加载到物理内存,然后通过页表映射到共享区。
在这里插入图片描述
此时线程就会从代码区转到共享区。
在这里插入图片描述
在共享区执行完成后,再转到我们的代码区。

在代码区中,我们的自己的代码,调用库的代码,系统调用的代码,所有代码的执行都是在进程的地址空间当中执行的

下面我们来看一下库和OS的关系:
在这里插入图片描述
线程的全部实现,并没有全部体现在OS内,而是OS提供的执行流,具体的线程结构由库来进行管理

既然需要管理,库就会创建相关的结构体:struct thread_info,里面存了线程id和私有栈等等。那么库就会在共享区把我们的结构体信息的虚拟起始地址返回,所以pthread_t对应的就是用户级线程的控制结构体的起始地址。

Linux中,线程库用户级线程库,和内核的LWP是1:1关系

主线程的独立栈结构,用的就是地址空间中的栈区。新线程用的栈结构,用的是库中提供的栈结构

3. 线程的局部存储

下面讲解一个概念,线程的局部存储是什么
我们知道:我们定义的全局变量是默认被全部线程共享的,如果我们想让某个全局变量变成私有,我们就需要定义线程的局部存储

那么我们该如何定义
在这里插入图片描述
我们把每个线程的global_val的值和地址都打印出来,并且设置一个变量看它的变化。

运行结果如下:
在这里插入图片描述
可以看出:每个线程的global_val的地址都是一样的,并且每个执行流都对同一个global_val进行++。

如果我们想让这个全局变量变成每个线程的私有,我们可以这样:
在这里插入图片描述
在变量前面加上__thread(记住是两个_)。
在这里插入图片描述
每个线程的global_val的地址都不一样了,并且只对自己的++。

我们知道:每个线程id在内核中都有对应的轻量级进程LWP,这个线程的id是给我们用户自己用的,我们不需要用LWP,如果我们就想要LWP,我们就需要绕过这个pthread线程库。
在这里插入图片描述
我们需要调用这个系统调用,但是这个我们不能直接调用,直接调用会出错。
在这里插入图片描述
我们需要用这个系统调用。
在这里插入图片描述
运行结果:
在这里插入图片描述

4. 分离线程

默认情况下,新创建的线程是joinable的(意思是可以等待的),线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
pthread_join的作用是:1。释放线程资源,前提是线程退出了。2.获取线程对应的退出码

如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
在这里插入图片描述
可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离:
在这里插入图片描述
joinable和分离是冲突的,一个线程不能既是joinable又是分离的

举个例子:
在这里插入图片描述
我们在每个自己的线程里给自己分离了。
在这里插入图片描述
然后我们把等待退出码再打印出来看一下。

运行结果如下:
在这里插入图片描述
我们可以看到线程分离后,线程还是在运行。
在这里插入图片描述
我们在这里加一个sleep看一下:
在这里插入图片描述
我们可以看到,出现错误了。

这是为什么呢
首先,我们要知道:CPU调度线程的运行是不确定的,一开始的情况是CPU先调度了主线程,然后去调度新线程,也就是说主线程先阻塞等待了,新线程还没来的及分离。
所以,我们更倾向于让主线程,分离其他线程

新线程分离,如果主线程先退出,那么进程就退出,新线程也就退出了。一般我们分离线程,对应的main thread一般不要退出(常驻内存的进程)

如何理解exit
在这里插入图片描述
我们在线程里面exit,看一下运行情况:
在这里插入图片描述
任何一个线程调用exit,都表示整个进程退出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学代码的咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值