Linux 多线程(一)线程概念:线程概念、线程与进程、线程间的独有与共享、多线程与多进程、线程控制

  1. 线程概念
  2. 线程与进程
  3. 线程间的独有与共享
  4. 多线程与多进程
  5. 线程控制

线程概念

什么是线程

线程是进程中的一条执行流,执行程序中的某部分代码。linux下没有具体实现的线程,只有库函数用pcb来实现的线程,所以可以认为,每个pcb就是一个线程,所以进程中都至少有一个线程,这些PCB共用进程中的同一个页表和虚拟地址空间,比传统的进程更加轻量化,所以这些线程在linux下也被称为轻量级进程

PCB也就是进程控制块,在Linux下PCB可以实现对程序的调度运行,所以可以将其作为一个执行流,来实现线程

为什么要使用线程呢?
  1. 创建一个新线程的代价要比创建一个新进程小得多
  2. 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  3. 线程占用的资源要比进程少很多

线程与进程

线程与进程

进程:是一个程序的动态执行,是系统资源分配的基本单位
线程:线程是进程中的一条执行流,是CPU调度的基本单位

在这里插入图片描述

我之前博客提到的进程其实都是单线程的进程,在linux其实每个pcb就可以理解为一个执行流,也就是一个线程

在这里插入图片描述
从这两幅图可以看出来,linux下的进程其实是一个线程组,一个进程中可以包含多个线程,每一个线程都是进程中的一条执行流(PCB),这些线程在进程的内部运行,本质也就是在进程的虚拟地址空间中运行

线程之间共用进程中的同一个虚拟地址空间,通过同一个页表来完成映射


线程间的独有与共享

既然线程都处于同一个进程中,共用同一个虚拟地址空间和页表,那么它们之间还有哪些数据共享,哪些数据独有呢?

独有:
  • 标识符(唯一的标识符来区分线程)
  • 栈(独有的函数栈,防止调用栈紊乱)
  • 寄存器(也就是PCB中的上下文数据,程序计数器,内存指针等)
  • 信号屏蔽字(因为信号会打断进程当前操作,让他优先处理信号,但是一个信号只需要一个执行流去执行即可,如果不想该线程被打断,则对该信号屏蔽,让其他线程去执行)
  • errno(系统调用完毕后重置的一个全局变量,防止被其他线程覆盖)
  • 优先级(各有各的调度优先级)
共享
  • 虚拟地址空间(代码段/数据段, 线程之间数据和代码都共享)
  • 文件描述符表(io信息)
  • 信号处理方式(信号是针对整个进程的,所以处理方式应该都一样)

多线程与多进程

如果需要进行多任务处理,有两种方法,一种是多线程,一种是多进程
多进程
在这里插入图片描述
多线程在这里插入图片描述

多线程的优点:
  • 线程的创建和销毁成本低(从上面的图可以看出来,创建多个线程只需要创建多个PCB)
  • 线程间调度成本低(使用同一个页表,调度切换时不用切换页表)
  • 线程间通信更加灵活(共用同一个虚拟地址空间,数据段共享,只需要获取地址即可访问数据)
多进程的优点:
  • 更具有健壮性,更加稳定(因为异常,系统调用,信号等都是对整个进程生效,一旦出现进程出问题会影响所有线程)
共同优点
  • IO密集型程序:多任务并行处理(单磁盘可以并行压缩IO等待事件/多磁盘可以实现同时处理)
  • CPU密集型程序:程序中进行大量的数据运算处理,CPU资源足够,则可以同时处理,提高效率(线程数为CPU核心数+1,多出来的一个是当某一线程阻塞时顶替用的。如果创建进程过多,会增加切换调度的成本)

线程控制

前面也说过了,Linux本身是没有线程的,只有库函数中通过PCB来模拟的线程。在用户态调用库函数创建一个线程,其本质就是在内核中创建一个轻量级进程来实现程序的调度。

创建线程:

int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void*),void *arg);

thread:输出型参数,用来获取线程ID–线程的操作句柄
attr:用于设置线程属性,通常置NULL
start_routine:函数指针,线程的入口函数,线程会去运行这个函数,函数运行结束后,线程退出。
arg:需要传递给线程的入口函数的参数
头文件:#include
返回值:成功返回0,失败返回非0


终止线程:

当线程执行完入口函数就会退出,但也可以通过调用对应的接口退出。

退出一个线程

void pthread_exit(void *retval);

和exit()不同,exit是退出整个进程

线程退出接口,哪个线程调用就退出哪个线程
retval:退出返回值
主线程退出,并不会导致进程退出,只有全部线程退出了进程才会退出。

终止一个线程

int pthread_cancel(pthread_t thread);

线程等待:

线程有一个默认的属性,joinable,处于这个属性的线程,退出后需要被其他线程等待获取返回值并且释放资源。
默认情况下,线程是必须被等待的,如果不等待,则会造成资源泄露

int pthread_join(pthread_t thread, void **retval)

等待指定线程退出,获取其返回值。
并且该函数会阻塞,线程没有退出则一直等待。

线程分离:

将joinable的属性修改为detach属性

joinable:退出后需要被其他线程等待获取返回值并且释放资源。
detach:退出后自动释放资源,不需要被等待

如果不想获取返回值,也不想等待线程退出,就可以使用线程分离

int pthread_detach(pthread_t pthread)

线程id

pthread_t pthread_self(void)

返回调用线程的tid

在这里就要提一提tid与pid的区别,以及对于一个进程,我们看到的进程的pid到底是什么

tid:用户态线程的id,是线程的操作句柄,其实就是线程这块空间的首地址。
pid:轻量级进程id,这就是用来模拟线程的pcb的id
tgid:线程组id,也就是主线程id,就是我们外面看到的进程的pid


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部