1.进程和线程的概念

计算机有5大基本组成部分,运算器,控制器,存储器,输入和输出。运算器和控制器封装到一起,加上寄存器组和cpu内部总线构成中央处理器(CPU)。cpu的根本任务,就是执行指令,对计算机来说,都是0,1组成的序列,cpu从逻辑上可以划分为3个模块:控制单元、运算单元和存储单元。这三个部分由cpu总线连接起来。


CPU的运行原理就是:控制单元在时序脉冲的作用下,将指令计数器里所指向的指令地址(这个地址是在内存里的)送到地址总线上去,然后CPU将这个地址里的指令读到指令寄存器进行译码。对于执行指令过程中所需要用到的数据,会将数据地址也送到地址总线,然后CPU把数据读到CPU的内部存储单元(就是内部寄存器)暂存起来,最后命令运算单元对数据进行处理加工。周而复始,一直这样执行下去。

2.并发和并行

并发:并发当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。

并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。

并发与并行的区别:

  • 并发不是同时发生,并行是同时发生。 并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
  • 并发是指一个处理器同时处理多个任务。并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
  • 并发是在同一个cpu上同时(不是真正的同时,而是看来是同时,因为CPU要在多个程序之间切换)运行多个程序。
  • 并行是每一个CPU运行一个程序。

3.多核、多CPU、CPU与多线程、多进程的关系

  • 进程是资源分配的最小单位,一个程序有至少一个进程。线程是程序执行的最小单位。一个进程有至少一个线程。
  • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
  • 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
  • 多cpu的运行,对应进程的运行状态;多核cpu的运行,对应线程的运行状态。
  • 单CPU中进程只能是并发,多CPU计算机中进程可以并行。
  • 单CPU单核中线程只能并发,单CPU多核中线程可以并行。
  • 一个进程中可以有多条执行路径同时执行,一个线程就是进程中的一条执行路径。

4.多进程(multi-processing) 和多线程(multi-threading)的区别

多进程是各个并行任务之间“不使用“共同的内存空间;而多线程的各个并行任务”使用“共同的内存空间。

(1)多进程

  • 优点:独立内存空间;实现代码直观简单;充分利用多核多CPU;避免全局解释器锁的限制;
  • 缺点:无法实现对象和内容共享;需要较大的内存空间。
  • 应用:一般用于数学计算、数据处理等场景,计算密集型(CPU密集型)任务指的是需要做大量的逻辑运算。

(2) 多线程

  • 优点:轻量,需要的额外内存较小;共享内存,方便访问;对于CPython解释器,可以通过全局解释器锁使用C扩展;适合I/O密集型任务。
  • 缺点:全局解释器锁的限制;并行任务不能杀掉;实现代码较为复杂。
  • 一般用于爬虫请求、文件读写等场景。IO密集型任务指的是输入输出型。

(3)实现方式

  • 实现多线程的方式:threading.Thread()、线程池concurrent.futures.ThreadPoolExecutor
  • 实现多进程的方式:multiprocessing.Process、进程池concurrent.futures.ProcessPoolExecutor

(4)多线程与多进程的应用场景不一样:

  • 线程的创建开销小、由于GIL的存在,无法真正并行,适合GUI、网络通信、文件读写等IO密集型场景;
  • 进程的创建开销大,可以充分利用多个CPU实现并行,适合计算量比较大(比如单个函数执行需要几分钟、几十分钟以上),且无需IO(简单地说就是数据已经在内存中,不需要读取磁盘、不需要网络通信)的场景。
  • 多线程、多进程都不适合的场景:基本不涉及IO或只读取一次文件这种,且计算量不大,单线程短时间就能结束(一两秒左右)的情况下,单进程单线程是最好的。

5.python的全局解释器锁GIL

Python 全局解释器锁或GIL,简单来说,是一个互斥锁(或锁),它只允许一个线程控制 Python 解释器。这意味着在任何时间点都只能有一个线程处于执行状态。执行单线程程序的开发人员看不到 GIL 的影响,但它可能是 CPU 密集型和多线程代码中的性能瓶颈。由于即使在具有多个 CPU 内核的多线程架构中,GIL 也只允许一次执行一个线程,因此 GIL 被称为 Python 的“臭名昭著”的特性,但是它确实为Python内存处理提供了方便。

如果一个对象同时被多个线程来引用,那么引用计数可能同时增加或减少,每个线程按照自己的方式进行计数,对象在整个内存中的引用变得十分混乱,很容易造成内存泄漏或者其他很多不可预见的Bug。一个解决办法给就是每个线程都给引用计数加一个锁,阻止别人修改,不过这样会造成锁死现象(比如一个对象有多个锁时),另外,大量的资源会浪费在加锁解锁的过程了,严重拖慢了程序运行速度。

这个时候,就需要一个统一来管理引用计数的机制,以确保对象引用计数准确、安全。全局解释器锁就是用来处理这种情况的。它既避免了不同线程带来的引用计数混乱,又避免了过多线程锁带来的死锁和运行效率低的问题。虽然全局解释器锁解决了对象引用计数的问题,但随之而来的是,很多CPU密集型任务在全局解释器锁的作用下,实际上变成了单线程,不能充分发挥CPU的算力,影响程序速度。

全局解释器锁并不是Python独有的,其他一些语言,比如Ruby也存在全局解释器锁。还有一些语言没有使用引用计数的方式来管理内容,而是使用垃圾回收机制(GC)来管理内存。虽然这样避免了全局解释器锁,但是在单线程处理上,GC并不占有优势。

全局解释器锁对多线程的影响

  • 首先应该区分不同性质的任务,有一些是CPU密集型的任务,有一些是I/O密集型的任务。
    CPU密集型的任务在最大程度上使用了CPU,比如数学矩阵的计算、图像处理、文件解压缩等。【一般使用多进程应对】
  • I/O密集型任务在需要花费很多时间来等待信息的输入和输出(读写),比如从网址下载内容,大量访问磁盘进行读写等。【一般使用多线程应对】

6.进程池

进程就是运行着的程序。写的python程序(或者其他应用程序比如画笔、qq等),运行起来,就称之为一个进程;在windows下面打开任务管理器,里面显示了当前系统上运行着的进程。这些程序还没有运行的时候,它们的程序代码文件存储在磁盘中,就是那些扩展名为 .exe 文件。双击它们,这些 .exe 文件就被os加载到内存中,运行起来,成为进程。

进程池:可以提供指定数量的进程给用户使用,即当有新的请求提交到进程池中时,如果池未满,则会创建一个新的进程用来执行该请求;反之,如果池中的进程数已经达到规定最大值,那么该请求就会等待,只要池中有进程空闲下来,该请求就能得到执行。

7.线程池

系统中每个进程里面至少包含一个 线程 。线程是操作系统创建的,每个线程对应一个代码执行的数据结构,保存了代码执行过程中的重要的状态信息。没有线程,操作系统没法管理和维护 代码运行的状态信息。所以没有创建线程之前,操作系统是不会执行我们的代码的。

8.同步和异步

  • 同步,就是调用某个东西时,调用方得等待这个调用返回结果才能继续往后执行。
  • 异步,和同步相反调用方不会等待得到结果,而是在调用发出后调用者可用继续执行后续操作,被调用者通过状体来通知调用者,或者通过回掉函数来处理这个调用

9.队列(线程间通信)

在一个进程中,不同子线程负责不同的任务,t1子线程负责获取到数据,t2子线程负责把数据保存的本地,那么他们之间的通信使用Queue来完成。因为再一个进程中,数据变量是共享的,即多个子线程可以对同一个全局变量进行操作修改,Queue是加了锁的安全消息队列。

Python的Queue队列,主要用于多生产者和消费者模式下的队列实现,特别适合多线程时的消息交换。通过使用队列,把生产者和消费者分解开来,作为其中的中间件,比如生产者产生一个数据,然后放到queue队列中,queue队列在把这个数据放到消费者线程中。使用单线程不必用队列,但是队列对于多线程来说是不可或缺的。

10.线程同步

如果没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。同步就是协同步调,按预定的先后次序进行运行。

"同"字从字面上容易理解为一起动作,其实不是,"同"字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。

实现线程同步的方式可以是:

  • 线程锁
  • 条件变量
  • 信号变量
  • 事件

11.生产者与消费者

生产者消费者模型具体来讲,就是在一个系统中,存在生产者和消费者两种角色,他们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者消耗数据或者资料。

举一个寄信的例子,假设要寄一封信,大致过程如下:

  1. 你把信写好-―相当于生产者制造数据;
  2. 把信放入邮简――相当于生产者把数据放入缓冲区;
  3. 递员把信从邮简取出—一相当于消费者把数据取出缓冲区;
  4. 递员把信拿去邮局做相应的处理――相当于消费者处理数据;

12.daemon和non-daemon线程

Python 中,构造线程的时候,可以设置daemon属性,这个属性必须再start()方法前设置好。线程daemon属性,如果设定就是用户的设置,否则就取当前线程的daemon值。主线程是non-daemon线程,即daemon = False。线程具有一个daemon属性,可以手动设置为True或者False,也可以不设置,则取默认值为None。

如果除主线程之外还有non-daemon线程的时候,主线程退出时,也不会杀掉所有daemon线程,直到所有non-daemon线程全部结束,如果还有daemon线程,主线程需要退出,会结束所有daemon线程,程序退出。

13.锁和共享内存

【多线程、多进程涉及读写必须要用锁,一定要用】

(1)多线程的线程锁

①死锁问题和解决
如果多个线程要调用多个现象,而A线程调用A锁占用了A对象,B线程调用了B锁占用了B对象,A线程不能调用B对象,B线程不能调用A对象,于是一直等待。这就造成了线程“死锁”。Threading模块中,也有一个类,RLock,称之为可重入锁。该锁对象内部维护着一个Lock和一个counter对象。counter对象记录了acquire的次数,使得资源可以被多次require。最后,当所有RLock被release后,其他线程才能获取资源。在同一个线程中,RLock.acquire可以被多次调用,利用该特性,可以解决部分死锁问题

②当多个线程同时访问一个数据时,需加锁,排队变成单线程一个一个执行,避免出错。
③加锁避免并发导致逻辑出错。

④每当一个线程a要访问共享数据时,必须先获得锁定;如果已经有别的线程b获得锁定了,那么就让线程a暂停,也就是同步阻塞;等到线程b访问完毕,释放锁以后,再让线程a继续。

⑤同一时刻只允许一个线程操作该数据,可以保证数据安全。 线程锁用于锁定资源,可以同时使用多个锁,当需要独占某一资源时,任何一个锁都可以锁这个资源。

⑥将一段代码锁住,一旦获得锁权限,除非释放线程锁,否则其他代码都无法获得锁权限。

(2)多进程的进程锁

①很多时候,需要在多个进程中同时写一个文件,如果不加锁机制,就会导致写文件错乱。
②谁先抢到锁谁先执行,等到该进程执行完成后,其它进程再抢锁执行。
③python多进程编程使用进程池非常的方便管理进程,但是有时候子进程之间会抢占一些独占资源,比如consol或者比如日志文件的写入权限,这样的时候我们一般需要共享一个Lock来对独占资源加锁。

1.1 异步相关概念:初步了解相关推荐

  1. gRPC 基础概念详解

    作者:jasonzxpan,腾讯 IEG 运营开发工程师 gRPC (gRPC Remote Procedure Calls) 是 Google 发起的一个开源远程过程调用系统,该系统基于 HTTP/ ...

  2. 《数据结构C语言版》——二叉树详解(图文并茂)

    哈喽!这里是一只派大鑫,不是派大星.本着基础不牢,地动山摇的学习态度,从基础的C语言语法讲到算法再到更高级的语法及框架的学习.更好地让同样热爱编程(或是应付期末考试 狗头.jpg)的大家能够在学习阶段 ...

  3. 使用篇丨链路追踪(Tracing)很简单:链路拓扑

    前文回顾: 基础篇|链路追踪(Tracing)其实很简单 使用篇|链路追踪(Tracing)其实很简单:请求轨迹回溯与多维链路筛选 使用篇丨链路追踪(Tracing)很简单:链路实时分析.监控与告警 ...

  4. 【2023年面试】JUC

    面试内容索引 多线程JUC 1. forkJoin分支合并框架 黑马Juc链接地址 尚硅谷Juc csdn实践 csdn原理1 原理2 原理3 普通线程池和forkjoin的区别? 2. Stream ...

  5. 【嵌入式开发】时钟初始化 ( 时钟相关概念 | 嵌入式时钟体系 | Lock Time | 分频参数设置 | CPU 异步模式设置 | APLL MPLL 时钟频率设置 )

    文章目录 一. 时钟相关概念解析 1. 相关概念术语 ( 1 ) 时钟脉冲信号 ( 概念 : 电压幅度 时间间隔 形成脉冲 | 作用 : 时序逻辑基础 间隔固定 根据脉冲数量可计算出时间 ) ( 2 ...

  6. Swift5多线程系列一GCD异步/延时使用初步封装

    Swift5多线程系列一GCD异步/延时使用初步封装 GCD异步延时使用初步目录 Swift5多线程系列一GCD异步/延时使用初步封装 前言 一.封装成基本的struct 二.基本使用 1.异步初步任 ...

  7. C4droid异步调用框架完成初步实现

    由于C4droid目前(C4droid 5.0 with GCC 5.3)不支持std::future和std::async,所以异步调用必须手动实现.于是,使用std::thread,借助宏定义和指 ...

  8. AsyncTask中各个函数详细的调用过程,初步实现异步任务

     AsyncTask内部类可能会产生内存泄露的问题 解决上述内部类可能引起的内存泄露问题的方法 将AsyncTask或者Thread的子类作为单独的类文件,不持有Activity的强引用 将Async ...

  9. 关于.NET异步调用的初步总结

    最近看了看.NET异步调用方面的资料,现择重点总结,若有纰漏敬请指正. 异步调用的实质: 异步调用通过委托将所需调用的方法置于一个新线程上运行,从而能够使一个可能需要较长时间的任务在后台执行而不影响调 ...

最新文章

  1. 了解CSS/CSS3原生变量var (转)
  2. 《Windows Server 2012 Hyper-V虚拟化管理实践》——1.3 物理服务器选型建议
  3. 知乎热问:国家何时整治程序员的高薪现象?
  4. 按键控制LED实现启动、停止按钮
  5. python向量机使用方法_Python中支持向量机SVM的使用方法详解
  6. Win7 如何访问XP系统里的网上邻居?
  7. 【Python面试】 说说Python中有几种数据类型?​
  8. vue 动态显示三级路由
  9. 鸿蒙系统上线 华为内部人员喷了,鸿蒙系统上线?华为人员表示:勿上当 山寨网站...
  10. 米度教育零基础三个月学会机器学习视频总结
  11. 企业微信和后台管理系统的结合管理(1)---各个系统之间的关系和协同。
  12. jquery ajax jsonp跨域调用实例代码
  13. pythonplatform标识_Python utils.platform方法代码示例
  14. jQuery上传文件控件Uploadify使用
  15. R在市场调查中的应用--探索性因子分析(EFA)
  16. 最新信息安全毕业设计题目选题推荐
  17. EfficientNet与EfficientDet的详解
  18. Clojure学习笔记(一)——介绍、安装和语法
  19. 大数据学习环境搭建系列(二)虚拟机软件Vmware的安装
  20. Android 开发 官方 论坛

热门文章

  1. rockwell ab 的AOP, EDS, FIRMWARE
  2. 下一个 Brython? 不, 是 Python in WebAseembly
  3. 企业案例丨腾讯广告助手 X 云开发CloudBase
  4. 70佳精美的 PSD 素材免费下载(上篇)
  5. 数据库系统概论---选择题刷题实训
  6. MATLAB-罗盘图绘制
  7. 全球最大移动运营商排行榜 中国移动居榜首 [含详表]
  8. 新手学习wpf记录 等级1
  9. 深度学习笔记------现阶段的目标检测器结构解析(Neck[FPN,PANet,Bi-FPN],Head[rpn,yolo...])
  10. Simulink仿真系列 - Simulink模块-数字基带调制解调模块详解 M-PAM,M-FSK,M-PSK