189 8069 5689

1.1异步相关概念:初步了解-创新互联

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来对独占资源加锁。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网站标题:1.1异步相关概念:初步了解-创新互联
本文路径:http://jkwzsj.com/article/eehsp.html

其他资讯