【Linux】——线程的概念和创建

1、线程概述

1.1线程的概念
在我们之前的所有观念里面,总是把进程定义成程序的执行实例,它并不执行什么,只有维护应用程序所需的各种资源,而线程才是真正的执行实体
在一个进程中多个执行线路叫做线程,进程必须至少包含一个线程。main函数执行的线程就叫做主线程,其他线程称之为函数线程。他们的区别就在于主线程是进程执行的入口。主线程和创建出来的函数线程是并发执行的。

1.2多线程与单线程

  1. 单线程:只有一条线程在执行任务,这种情况在我们日常的生活学习中很少遇到
  2. 多线程:创建多条线程同时执行任务。由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的。由此要引发线程调度和同步问题,在后续的博文中我们会陆续讲解。
  3. 注意!!!多线程并不是在任何情况下都能提升效率,比如说对于单核CPU计算密集型任务。因为线程本身创建和切换的开销。但是对于频繁IO操作的序列,多线程可以有效的并发,比如生产者、消费者问题
  4. 当增加一个线程的时候,增加的额外开销要小于该线程能够消除的阻塞时间,使用多线程才是有必要的。
    在这里插入图片描述

1.3多线程的优缺点
优点:提高CPU 的使用率、提高程序的工作效率
缺点:大量的线程会影响性能,因为CPU需要在他们之间切换。需要更多的内存空间。会出现线程安全或者死锁问题。

1.4多进程的优缺点
优点:
每个进程相互独立,不影响主程序的稳定性。通过增加CPU可以实现并行,扩充性能。多进程间使用的同步机制少,所以性能较高
缺点:
逻辑控制复杂,进程间通信不太方便。

1.5多线程和多进程的区别
具体的参照博文进程线程等相关知识

1.6线程管理
线程共享一个进程空间,因此很多资源是共享的,这些共享的资源显然不需要存放在线程控制块中,而是存放在进程控制块
因为创建线程的目的就是要经常协作,应当让共享的资源越多越好。
一般情况下线程共享的资源和独享的资源如下表所示:

线程共享资源 线程独享资源
地址空间 程序计数器(pc)用于存放下一条指令所在单元的地址的地方
代码区、数据区、堆 寄存器
文件描述符
子进程、信号等信息 状态字

2、线程的实现方式

值得注意的是,我们发现在之前讲述进程的时候并没有提到过实现方式的问题。
这是因为进程是在CPU上实现并发,而CPU是由操作系统管理操作的,因此进程的实现只能由操作系统内核来进行,而不存在用户态实现的情况。
所以下述的三种方式是在我们一般的操作系统当中,线程的管理方式。

2.1操作系统的实现方式

1、用户级线程
在这里插入图片描述
这种管理方式线程的管理都是在用户态管理的,存在一个线程库,包含了线程的创建、调度、管理和销毁,在这个线程库中除了正常执行任务的线程外,还有一个专门负责线程调度的线程。
对于操作系统内核而言,感知不到线程的存在,感知的是一个进程,并不知道多线程的存在。这种方式的优缺点如下:
优点:

  1. 灵活性:因为操作系统不用知道线程的存在,所以在任何操作系统上都能应用
  2. 线程切换快:因为切换在用户态进行,无需陷入到内核态
  3. 不用修改操作系统,实现简单

缺点:

  1. 编程变得诡异:因为用户态线程需要相互合作才能运转,这样在写程序的时候必须仔细斟酌在什么时候应该让出CPU给别的线程使用
  2. 在执行过程中,如果一个线程受阻,他将无法将控制权交出来,导致整个进程都无法推进

2、内核级线程
在这里插入图片描述
内核级线程就是线程的创建和管理都是在内核实现的。这样操作系统同时保有进程控制块和线程控制块。
优点:
用户编程简单:因为线程的复杂性由操作系统承担。
缺点:

  • 效率较低:每次线程切换都需要陷入内核,由操作系统来调度
  • 占用内核稀缺的内核资源:如果内核空间溢出,操作系统将停止运转。

3、组合级线程
在这里插入图片描述
这种方式的实现过程就是用户态执行系统负责进程内部线程在阻塞时的切换内核态的操作系统负责阻塞线程的切换
即我们同时实现内核态和用户态线程管理。
这样有了上述优点也避免了上述缺点。

2.2Linux系统的线程实现方式

从内核的角度来说,它并没有线程这个概念。Linux把所有的线程都当成进程来实现。线程仅仅被视为一个与其他进程共享某些资源的进程每个线程都拥有唯一隶属于自己的task_struct.所以在内核中,它看起来就像是一个普通进程(只是线程和其他进程共享某些资源,如进程的地址空间.data .bss .text .heap)
“轻量级进程”这种叫法本身就概括了Linux和其他系统的差别:

  • 在其他系统中,相当于重量级进程,线程被抽象成一种耗费较少资源,运行速度快的执行单元
  • 而对于Linux来说,它只是一种进程间共享资源的手段。

线程的创建
创建线程、创建进程调用的内核底层函数是一样的:传入参数标志调用系统调用函数clone,由clone调用do_fork实现,只不过传入的参数标志不一样

clone(CLONE_VM | CLONE_PS | CLONE_FILES | CLONE_SIGHAND,0);

传递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共享的资源种类。对比一下,一个普通的fork()实现和vfork()的实现如下:

clone(SIGCHLD,0);
clone(CLONE_VFORK | CLONE_VM |SIGCHLD,0);

3、线程、进程、协程之间的区别和联系

1、进程
直观点来说,保存在硬盘上的程序运行过后,会在内存空间里形成一个独立的内存体,这个内存体有自己独立的地址空间,与自己的堆,上级挂靠的是操作系统。
操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位

在这里插入图片描述
2、线程
也叫做轻量级进程,是操作系统调度(CPU调度)的最小单位

3、协程
是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)
在这里插入图片描述

举个栗子来说
我们都知道子程序调用是通过栈实现的,一个线程就是一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。
协程在子程序内部是可中断的,然后转而执行别的子程序,在适当的时候再返回来接着执行。

def A():
    print '1'
    print '2'
    print '3'

def B():
    print 'x'
    print 'y'
    print 'z'

假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:1 2 x y 3 z。

优点

  1. 极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此没有线程切换的开销。
  2. 不需要多线程的锁机制

4、进程和线程的区别与联系
区别:

  1. 根本区别:进程是资源分配的最小单元,线程是CPU调度的最小单位
  2. 内存层面:操作系统每创建一个进程,会为其分配不同的地址空间;来存储程序所占的资源,进程相互独立存在。线程共享进程的地址空间,操作系统只为它分配很小一部分内存。
  3. 包含关系:一个进程可以包含多个线程但是至少得有一个线程存在,但是一个线程只能属于一个进程。
  4. 系统开销:进程的切换开销很大,需要地址空间的切换,进程内核栈的切换以及进程用户堆栈以及寄存器的切换。线程切换开销小,因为共享进程的地址空间,所以在切换的时候只需要切换线程栈和PC寄存器。
  5. 通信方面:进程之间的通信需要通过IPC,而同一个进程的各线程之间可以直接通过传递地址或全局变量的方式传递变量
  6. 安全方面:进程之间不存在安全问题,但是线程因为其共享大的原因,存在安全问题。

一个形象的例子来解释他们的区别
在这里插入图片描述
把整条道路看成是一个“进程”,各个车道就是进程中的各个“线程”

  1. 这些线程(车道)共享了进程(道路)的公共资源(土地资源)
  2. 线程不能脱离了进程而存在(也就是说没有了道路,车道也没有了意义)
  3. 这些线程(车道)之间依靠代码逻辑(交通灯)来控制运行,一旦代码逻辑控制有误(死锁),那么线程就将陷入混乱,无序当中
  4. 这些线程(车道)之间谁先运行是未知的,只有在线程刚好被分配到CPU事件片(
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值