【Linux线程】——线程概念线程接口
目录
前言
1.线程
2.线程的本质
3.Linux线程库
3.1创建线程——pthread_create
3.2线程终止——pthread_exit
3.3线程等待——pthread_join
3.4线程分离——pthread_detach
3.5获取线程tid——pthread_self
4.线程的优缺点
4.1线程的优点
4.2线程的缺点
结语
前言
在当今复杂而高效的计算机系统中,多任务处理已经成为了不可或缺的核心能力。Linux操作系统以其卓越的性能和广泛的适应性,成为了众多开发者和系统管理员的首选平台。
而线程,作为进程中的执行单元,是实现多任务并发执行的关键技术之一。Linux线程的实现机制为开发者提供了丰富的工具和接口,使得我们能够充分利用系统资源,提高程序的运行效率和响应速度。
本文将深入探讨Linux线程的相关概念、原理和实践应用,为读者呈现一个全面而深入的Linux线程技术体系。
1.线程
线程是调度的基本单位,是轻量化的进程,是依附进程存在的产物
一个进程内部可以有多个线程,一个线程就是一个执行流,负责执行进程分配的任务
我们常说CPU调度进程,而在相应的进程获得CPU调度,得到时间片,这个时间片会再度被内部的线程进行瓜分,所以,线程才是CPU调度的基本单位
为什么会有线程呢?
父进程创建子进程的目的——让子进程完成其他任务
同理,创建线程的初衷也是如此,线程是一个执行流,可以异步完成进程交付的任务
但我们创建一个子进程不行吗?为什么还要线程?
进程的创建、切换、销毁、存在着大量的时空开销,为了减少这部分开销,提高工作效率,就创建了线程!
从创建上提高:
- 创建一个进程,操作系统会为其分配一个程序地址空间
- 创建一个线程,直接让其使用进程的程序地址空间
从切换上提高:
- 旧进程换出,新进程换入,寄存器会替换一套完整的进程上下文,但在实际当中,根据局部性原理,有一些进程的上下文是其实是存在相同的,有一部分寄存器也就不用替换
- 进程仍是一意孤行,这样就会多出来一部分CPU与内存的交互,增加时间开销
- 线程则是能省则省,减少了一部分寄存器的替换,减少了一部分时间开销
我们经常提到多线程,而不提多进程,就是这个原因,使用多线程比使用多进程更加高效率,更加优雅
简单总结一下:
线程是轻量化的进程,是进程内部一个独立的执行流,可以异步完成任务,且效率更高
2.线程的本质
先说结论:
线程的本质就是一个函数的运行
可以说,原来没有进程,有了线程,也便有了进程
我们常说的main函数,是程序的入口,是进程的起点,其实也是线程的起点
只不过这个线程比较特殊,是进程的第一个线程,我们将其称为主线程
主线程可以创建其他线程
正如主线程的入口是main函数,其他线程入口也是一个函数
而线程的运行,就是函数的运行,创建一个线程,本质上也是调用一个函数,只不过这个函数的调用并不是和主线程同步的,而是异步!
线程的资源分配
线程不会有自己的程序地址空间,而是从进程地址空间分配,那么哪些资源会被线程共享,哪些资源是线程自己的呢?
直接紧扣字眼,记住:线程的本质是函数的运行,线程CPU调度的基本单位
什么是线程私有的?
- 线程的本质是函数的运行,当函数被调用的时候,会有自己的函数栈帧,上面有函数运行所需要的一切,同理,线程也会有自己的栈区,即,线程的栈区是私有化的
- 线程是CPU调度的基本单位,当被CPU调度的时候,会有一套寄存器内容,这套内容即是线程的上下文,调出带走,调入带进,即线程上下文是私有的
什么是线程共有的?
在main外定义一个函数,这个函数内部可以使用外部的什么?
全局变量、static变量、函数等
答案也显而易见,main函数外的普通函数可以共享什么,线程就可以共享什么
代码区、静态区、堆区都是每个线程可以访问并使用的,不过有读写的限制
3.Linux线程库
已经理解了什么是线程,以及它的特点,接下来看看如何使用
在Linux系统中,有多种线程库可供使用,其中最常用的是POSIX线程库(也称为Pthreads)。Pthreads是一个遵循POSIX标准的线程库,它提供了一组丰富的函数和数据类型,用于创建、管理和同步线程。
下面介绍一些常用的Pthreads接口及其功能
3.1创建线程——pthread_create
pthread_create函数用于创建一个新的线程,包含于头文件<pthread.h>中
函数造型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数说明:
hread
:用于存储新创建线程的线程ID。attr
:线程属性对象,通常可以设置为NULL
,表示使用默认属性。start_routine
:线程启动后要执行的函数指针。arg
:传递给线程函数的参数。
返回值说明:
- 返回值为0,则创建成功
- 返回值为-1,则创建失败,并重置error全局变量
基本使用:
void *print_hello(void *arg) {
printf("Hello from thread!");
return NULL;
}
int main() {
pthread_t thread;
int ret = pthread_create(&thread, NULL, print_hello, NULL);
if (ret!= 0) {
perror("pthread_create");
return 1;
}
}
使用技巧:
线程的本质就是函数的运行,就将创建线程当成异步调用函数即可,定义函数时,就将其当成main来写即可,线程并不是很高大尚的东西,紧扣字眼就不难
3.2线程终止——pthread_exit
pthread_exit用于终止一个线程的执行,包含于头文件<pthread.h>中
函数造型:
void pthread_exit(void *retval);
参数说明:
-
retval
:线程的返回值,可以被其他等待该线程结束的线程获取。 -
**
pthread_join
函数**:用于等待指定线程结束并获取其返回值。其原型如下:
基本使用:
void *print_hello(void *arg) {
printf("Hello from thread!");
void* ret_value = new void;
pthread_exit(ret_value);
return NULL;
}
注意:
这个参数并不是创建者调用的,而是需要终止的线程自己调用的,相当于“我终结我自己”,函数成功后,会将线程的返回值保存在指针中,由其他线程获取
3.3线程等待——pthread_join
我们创建线程的目的是为了让新的线程完成其他任务
不仅要交付给线程这个任务,我们还需要这个任务被线程完成得怎么样。
对一个线程,需要获取线程的结果
已经退出的线程,其空间没有被释放,仍然在进程的地址空间内,会一直占据这部分空间。
当新的进程被创建,就不会复用这部分空间,
对一个线程,我们需要主动回收已经退出的进程的空间资源,防止空间泄露
为了完成上面两个目标,我们可以使用线程库中的 pthread_join函数 一键完成
pthread_join
函数是 POSIX 线程(Pthreads)库中用于等待指定线程结束并获取其返回值的重要函数。它在多线程编程中扮演着关键角色,确保主线程能够正确地同步和回收子线程的资源。
函数造型:
int pthread_join(pthread_t thread, void **retval);
参数说明:
-
thread
(pthread_t
类型): 要等待结束的线程的线程 ID。 -
retval
(void **
类型): 指向一个void *
指针的指针,用于存储被等待线程通过pthread_exit
返回的值。如果不需要获取返回值,可以将其设置为NULL
。
返回值说明:
- 成功时: 返回
0
。 - 失败时: 返回错误码,常见的错误码包括:
注意:
pthread_join函数一般是创建新线程的线程使用的,即一般使用者都是主线程
3.4线程分离——pthread_detach
使用pthread_join等待指定线程的目的之一——知晓线程执行任务的结果
倘若我们并不关心一个线程的执行任务的结果,此时用pthread_join进行等待实际上就是一种负担,此时就可以使用pthread_detach函数对目标线程进行分离
pthread_detach函数在 POSIX 线程(Pthreads)库中用于将一个线程设置为“分离状态”,处于分离状态的线程在终止时会自动释放其资源,主线程无法也不需要通过 pthread_join
来回收其资源。
函数造型:
int pthread_detach(pthread_t thread);
参数说明:
thread
(pthread_t
类型): 要设置为分离状态的线程的线程 ID。
返回值说明:
- 成功时: 返回
0
。 - 失败时: 返回错误码
注意:
线程分离可以是主线程将目标线程分离,也可以是线程主动将自己分离
3.5获取线程tid——pthread_self
pthread_self
是 POSIX 线程(Pthreads)库中的一个函数,用于获取调用线程的线程 ID。
每个线程在创建时都会被分配一个唯一的线程 ID,pthread_self
可以用来获取当前线程的这个 ID。
函数造型
pthread_t pthread_self(void);
返回值
- 返回值类型:
pthread_t
注意:
返回调用线程的线程 ID。这是一个不透明的数据类型,具体实现可能因系统而异。
4.线程的优缺点
凡事都有两面性,对待线程我们也不能光看它的优点
需要辩证地看待它
4.1线程的优点
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多
- 能充分利用多处理器的可并行数量
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
4.2线程的缺点
- 性能损失:一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
- 健壮性降低:编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
- 缺乏访问控制:进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
- 编程难度提高:编写与调试一个多线程程序比单线程程序困难得多。
结语
本文对Linux线程做了一个简要的概述,希望能让读者对Linux线程的基本概念和操作有了一定的了解。线程技术是Linux编程中不可或缺的一部分,在今后的学习和实践中,希望大家都能继续深入研究,将其运用到实际的开发项目中。以上就是本文的全部内容,感谢大家的阅读。