Linux 多线程(1)线程概念与线程控制

多线程:概念、线程控制(创建、终止、等待、分离),线程安全(问题&实现),应用(生产者与消费者模型,线程池,单例模式)

(重要,因为多线程在实际工作使用较多,所以面试问的多)

1.概念

--线程是进程中的一个执行流程

线程是cpu进行执行调度的基本单元(调度一段代码的执行是通过线程完成的)

--进程是系统进行资源分配的基本单元

Linux下一个进程中是可以存在多个pcb的,一个pcb就是一个执行流程

一个进程中有多个pcb,和多个进程中有多个执行流程使用中有什么区别?

例子:零件加工厂,进行零件加工

多进程:如果有很多的零件加工,就相当于多开几个工厂,多个厂子可以同时加工零件

多线程:如果有很多的零件加工,就在一个 厂子里面,多开几条生产线。

哪个更好?

多进程---多建厂子:资源消耗大,更稳定,健壮

多线程--多开生产线:资源消耗小,健壮性不如多进程

线程到底是什么

线程是cpu调度执行的基本单元,而linux下pcb是程序运行过程的描述,因此linux下的线程通过pcb实现的,学了线程就知道一个进程有多个pcb的。(其他系统下进程有进程的描述,线程有线程的描述 )

因此有些人说linux下没有真正的线程,因为linux下的线程是一个pcb,被称为轻量级进程-LWP

多进程:占用资源多,但是健壮稳定

多线程:占用资源少,但是健壮性低,写代码需要小心

进程与线程的区别

进程是系统进行资源分配的基本单元(每运行一个程序,操作系统就要分配一次程序运行所需的资源)

线程是cpu进行执行调度的基本单元,在linux下是通过pcb实现的,一个进程中可以有多个pcb,被称作轻量级进程 LWP

多线程和多进程在多任务中处理中的优缺点:

多进程:健壮,稳定

多线程:

1.共享了虚拟地址空间,因此线程间通信更加灵活(包括进程间通信在内,还有全局变量,函数传参)

2.创建和销毁成本更低(线程之间很多资源都是共享的,创建一个线程不需要分配太多资源)

3.同进程的线程间调度成本更低(CPU上加载的块表信息,页表指针...不需要替换)

适用场景

对于程序的安全性要求大于性能和资源要求,使用多进程(比如shell);其他使用多线程

多个线程在同一进程中同时运行为什么不会混乱?

  1. 其实每个线程调度执行的就是一个函数

vfork--创建子进程,父子进程公用同一个虚拟地址空间,为了避免出现运行混乱,因此父进程阻塞直到子进程程序替换或者退出

  1. 多线程关于栈执行出现混乱的解决方案:把所有可能混乱的地方,给每个线程都单独整一份

线程之间的独有信息:标识符、栈、上下文数据、信号屏蔽字

给一个进程发送一个信号,进程中有多个线程(pcb),谁处理这个信号?--谁有时间谁就去(谁拿到时间片正在运行就是谁)

线程之间的共享信息:虚拟地址空间、文件描述符、信号处理方式、工作路径、用户ID、组ID

2.多任务处理

在多任务处理中,使用多执行流完成的优点:更加充分利用计算机资源,提高任务处理效率

那么在多任务处理中启动多少执行流比较合适呢?

执行流不是越多越好:因为执行流越多,cpu切换调度越频繁,执行流太多,返回会造成切换调度消耗大部分资源,启动多少执行流没什么固定数量,压力测试即可,不同程序对CPU要求不一样。

在任务处理中,程序分为两种程序:

  1. cpu密集型程序:一段数据中几乎都是数据的运算(对CPU使用率高)

  1. IO密集型程序:一段程序中大部分都是IO操作(大部分时间都是进行IO操作或者等待,对CPU使用率不高)

3.线程控制

主要为线程操作接口(创建、终止、等待、分离)

liunx下线程操作接口都是库函数,因为linux操作系统并没有直接向上层提供线程的系统掉用接口,直接基于系统的调用接口封装实现线程的相关接口。

线程库

与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的

要使用这些函数库,要通过引入头文<pthread.h>

链接这些线程函数库时要使用编译器命令的“-lpthread”选项

创建

int pthread_create(pthread_t *tid, pthread_attr_t *attr, void *(routine)(void*), void *arg);

tid:传入一个pthread_t类型变量的空间地址。用于接收线程ID--线程的操作句柄

attr:设置线程的属性,大部分属性都不用管;后面会有一个分离属性使用单独接口进行设置,attr为NULL表示使用默认属性

routine:是个函数指针,传入线程入口函数的地址,这个线程调度运行的就是这个函数

arg:给线程启动函数routine传入的参数

功能-----创建一个线程,指定这个线程要运行的函数routine,并给这个函数传入一个数据arg

返回值:成功返回0;失败返回错误码(非0)

注意:线程被创建出来后,谁先运行不一定 ,取决于操作系统的调度

线程只是一个执行流,说白了就是一个机器人,他做什么,什么时候 做,都是程序员来指定的

函数是一段功能的集合

线程是个执行流,线程是调度一个函数运行的

线程信息的查看

ps-L 选项进行查看(查看到其实是轻量级进程信息)

在多线程程序中,一个进程里面有多个pcb,当ps查看进程信息的时候应该显示谁的?

一个进程运行起来,默认会创建一个线程(pcb),这个线程有自己的pid

如果下边通过pthread_create创建一个线程(pcb),这个线程也有自己的pid,真正使用p查看进程信息的时候查看的是主线程pcb对应的信息

终止

线程终止:如何退出一个线程的运行

线程其实调度运行的是创建时所传入的入口函数,因此其实线程入口函数运行完了,线程就退出了

  1. 在线程入口函数中return;

注意:main中return退出的不仅是主线程,而是整个进程

  1. 在任意位置调用接口: void *pthread_exit(void *retval);

retval:用于设置线程的退出返回值

(上面两种方式都是主动退出,谁调用,谁退出)

  1. 在任意位置调用接口: int pthread_cancel(pthread_t tid);

注意:这个接口用来取消指定线程运行的,给谁tid,推出谁

一个线程如果是被取消的,则他的返回值就不是一个正经的返回值 了

等待

  1. 主线程退出,其实不影响其他线程的运行(不多见)

所有的线程退出了,则进程退出释放所有资源;(所有生产线停了,厂子没必要存在)

进程退出了,会先退出所有的线程;(一个厂子塌了,所有生产线也都坏了)

  1. 其实一个线程退出了,资源也并没有完全释放(因为要保存返回值)

僵尸进程:子进程退出了,为了获取退出码,因此没有直接释放资源,等待父进程处理

等待:等待指定的线程退出,获取退出线程的返回值,回收退出线程的所有资源

线程之间传递数据要尤其注意数据的生命周期

如果一个线程是被取消的,则获取的返回值是PTHREAD_CANCELED 本质是个 (void*)-1

分离

在线程属性中,有一个分离属性,默认值是joinable,表示线程退出后,不会自动释放资源,需要被其他线程等待。

但是我们并不关心一个线程的返回值,也不想等待他的退出,则这时候将这个分离属性设置为detach状态;

detach状态,表示线程退出后,自动释放所有资源,不需要被等待(资源是自动释放的,因此也不能被等待--等待会出错)

接口: int pthread_detach(pthread_t tid);//设置指定线程的分离属性为detach

猜你喜欢

转载自blog.csdn.net/weixin_59215611/article/details/130410655