进程、线程、协程

1、进程

进程是具备必定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每一个进程都有本身的独立内存空间,不一样进程经过进程间通讯来通讯。因为进程比较重量,占据独立的内存,因此上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。html

 

2、线程python

线程的出现是为了下降上下文切换的消耗,提升系统的并发性,并突破一个进程只能干同样事的缺陷,使到进程内并发成为可能。假设,一个文本程序,须要接受键盘输入,将内容显示在屏幕上,还须要保存信息到硬盘中。若只有一个进程,势必形成同一时间只能干同样事的尴尬(当保存时,就不能经过键盘输入内容)。如有多个进程,每一个进程负责一个任务,进程A负责接收键盘输入的任务,进程B负责将内容显示在屏幕上的任务,进程C负责保存内容到硬盘中的任务。这里进程A,B,C间的协做涉及到了进程通讯问题,并且有共同都须要拥有的东西——-文本内容,不停的切换形成性能上的损失。如有一种机制,可使任务A,B,C共享资源,这样上下文切换所须要保存和恢复的内容就少了,同时又能够减小通讯所带来的性能损耗,那就行了。是的,这种机制就是线程。线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程当中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。线程的引入减少了程序并发执行时的开销,提升了操做系统的并发性能。线程没有本身的系统资源。线程的切换是根据调度算法时间片来的程序员

多线程的实现分为三类:web

用户级线程(User Level ThreadULT):对于这种线程的建立、撤消、和切换,由用户程序来实现,内核并不知道用户级线程的存在。算法

内核级线程(Kernel Level Thread KLT):它们是依赖于内核的,即不管是用户进程中的线程,仍是系统进程中的线程,它们的建立、撤消、切换都由内核实现。编程

混合式线程:同时支持ULT和KLT两种线程。api

 

3、协程安全

协程是一种比线程更加轻量级的存在,最重要的是,协程不被操做系统内核管理,协程的切换是由程序员本身调度的。网络

运行效率极高,协程的切换彻底由程序控制,不像线程切换须要花费操做系统的开销,线程数量越多,协程的优点就越明显。多线程

协程不须要多线程的锁机制,由于只有一个线程,不存在变量冲突。

对于多核CPU,利用多进程+协程的方式,能充分利用CPU,得到极高的性能。

优点:

最大的优点就是协程极高的执行效率。由于子程序切换不是线程切换,而是由程序自身控制,所以,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优点就越明显。

第二大优点就是不须要多线程的锁机制,由于只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只须要判断状态就行了,因此执行效率比多线程高不少。

 

可参考博客:https://blog.csdn.net/mieleizhi0522/article/details/82142856

 

Greenlet

greenlet是一个用C实现的协程模块,相比与python自带的yield,它可使你在任意函数之间随意切换,而不需把这个函数先声明为generator

Gevent: (实现遇到IO自动切换)

Gevent 是一个第三方库,能够轻松经过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet所有运行在主程序操做系统进程的内部,但它们被协做式地调度

 

4、区别 

一、进程多与线程比较

线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:
1) 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有本身独立的地址空间
2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
3) 线程是处理器调度的基本单位,但进程不是
4) 两者都可并发执行

5) 每一个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,可是线程不可以独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制

二、协程多与线程进行比较

1) 一个线程能够多个协程,一个进程也能够单独拥有多个协程,这样python中则能使用多核CPU。

2) 线程进程都是同步机制,而协程则是异步

3) 协程能保留上一次调用时的状态,每次过程重入时,就至关于进入上一次调用的状态

 

  • 与线程不一样,协程是本身主动让出CPU,并交付他指望的下一个协程运行,而不是在任什么时候候都有可能被系统调度打断。所以协程的使用更加清晰易懂,而且多数状况下不须要锁机制。
  • 与线程相比,协程的切换由程序控制,发生在用户空间而非内核空间,所以切换的代价很是的小。
  • 某种意义上,协程与线程的关系相似与线程与进程的关系,多个协程会在同一个线程的上下文之中运行。

 

5、进程调度算法

不一样环境的调度算法目标不一样,所以须要针对不一样环境来讨论调度算法。

1. 批处理系统

批处理系统没有太多的用户操做,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。

1.1 先来先服务 first-come first-serverd(FCFS)

按照请求的顺序进行调度。

有利于长做业,但不利于短做业,由于短做业必须一直等待前面的长做业执行完毕才能执行,而长做业又须要执行很长时间,形成了短做业等待时间过长。

1.2 短做业优先 shortest job first(SJF)

按估计运行时间最短的顺序进行调度。

长做业有可能会饿死,处于一直等待短做业执行完毕的状态。由于若是一直有短做业到来,那么长做业永远得不到调度。

1.3 最短剩余时间优先 shortest remaining time next(SRTN)

按估计剩余时间最短的顺序进行调度。

2. 交互式系统

交互式系统有大量的用户交互操做,在该系统中调度算法的目标是快速地进行响应。

2.1 时间片轮转

将全部就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程能够执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便中止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。

时间片轮转算法的效率和时间片的大小有很大关系:

  • 由于进程切换都要保存进程的信息而且载入新进程的信息,若是时间片过小,会致使进程切换得太频繁,在进程切换上就会花过多时间。

  • 而若是时间片过长,那么实时性就不能获得保证。

 
 

2.2 优先级调度

为每一个进程分配一个优先级,按优先级进行调度。

为了防止低优先级的进程永远等不到调度,能够随着时间的推移增长等待进程的优先级。

2.3 多级反馈队列

一个进程须要执行 100 个时间片,若是采用时间片轮转调度算法,那么须要交换 100 次。

多级队列是为这种须要连续执行多个时间片的进程考虑,它设置了多个队列,每一个队列时间片大小都不一样,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,以前的进程只须要交换 7 次。

每一个队列优先权也不一样,最上面的优先权最高。所以只有上一个队列没有进程在排队,才能调度当前队列上的进程。

能够将这种调度算法当作是时间片轮转调度算法和优先级调度算法的结合。

 
 

3. 实时系统

实时系统要求一个请求在一个肯定时间内获得响应。

分为硬实时和软实时,前者必须知足绝对的截止时间,后者能够容忍必定的超时。

 

6、进程间通讯

参考:https://blog.csdn.net/sinat_38682860/article/details/80483533

进程间通讯(IPC,InterProcess Communication)是指在不一样进程之间传播或交换信息。

IPC的方式一般有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不一样主机上的两个进程IPC。

以Linux中的C语言编程为例。

(1)管道pipe

  1. 它是半双工的(即数据只能在一个方向上流动),具备固定的读端和写端。

  2. 它只能用于具备亲缘关系的进程之间的通讯(也是父子进程或者兄弟进程之间)。

  3. 它能够当作是一种特殊的文件,对于它的读写也可使用普通的read、write 等函数。可是它不是普通的文件,并不属于其余任何文件系统,而且只存在于内存中。

(2)消息队列Queue

操做系统也提供了跨进程的消息队列对象可让咱们直接使用,只不过python没有默认提供包装好的api来直接使用。咱们必须使用第三方扩展来完成OS消息队列通讯。第三方扩展是经过使用Python包装的C实现来完成的。

OS消息队列有两种形式,一种是posix消息队列,另外一种是systemv消息队列,有些操做系统二者都支持,有些只支持其中的一个

(3)共享内存SharedMemory

共享内存也是很是常见的多进程通讯方式,操做系统负责将同一份物理地址的内存映射到多个进程的不一样的虚拟地址空间中。进而每一个进程均可以操做这分内存。考虑到物理内存的惟一性,它属于临界区资源,须要在进程访问时搞好并发控制,好比使用信号量。咱们经过一个信号量来控制全部子进程的顺序读写共享内存。咱们分配一个8字节double类型的共享内存用来存储极限的和,每次从共享内存中读出来时,要使用struct进行反序列化(unpack),将新的值写进去以前也要使用struct进行序列化(pack)。每次读写操做都须要将读写指针移动到内存开头位置(lseek)。

(4)套接字

这是一种更为通常得进程间通讯机制,它可用于网络中不一样机器之间的进程间通讯,应用很是普遍。

 

7、线程间同步

 

 

8、进程和线程、协程在python中的使用

  一、多进程通常使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,固然生产者消费者这种也可使用。多进程的优点就是一个子进程崩溃并不会影响其余子进程和主进程的运行,但缺点就是不能一次性启动太多进程,会严重影响系统的资源调度,特别是CPU使用率和负载。使用多进程能够查看文章《python 多进程使用总结》。注:python2的进程池在类中的使用会有问题,须要把类函数定义成全局函数。具体可参考 http://bbs.chinaunix.net/thread-4111379-1-1.html

  二、多线程通常是使用threading库,完成一些IO密集型并发操做。多线程的优点是切换快,资源消耗低,但一个线程挂掉则会影响到全部线程,因此不够稳定。现实中使用线程池的场景会比较多,具体可参考《python线程池实现》。

  三、协程通常是使用gevent库,固然这个库用起来比较麻烦,因此使用的并非不少。相反,协程在tornado的运用就多得多了,使用协程让tornado作到单线程异步,听说还能解决C10K的问题。因此协程使用的地方最多的是在web应用上。

总结一下就是IO密集型通常使用多线程或者多进程,CPU密集型通常使用多进程,强调非阻塞异步并发的通常都是使用协程,固然有时候也是须要多进程线程池结合的,或者是其余组合方式。

 

9、GIL(全局解释器锁)

GIL加在cpython解释器中, 其余的python解释器不会有GIL。

Python中的线程是操做系统的原生线程,Python虚拟机使用一个全局解释器锁(Global Interpreter Lock)来互斥线程对Python

虚拟机的使用。为了支持多线程机制,一个基本的要求就是须要实现不一样线程对共享资源访问的互斥,因此引入了GIL。

GIL:在一个线程拥有了解释器的访问权以后,其余的全部线程都必须等待它释放解释器的访问权,即便这些线程的下一条指令并不

会互相影响。

在调用任何Python C API以前,要先得到GIL

GIL缺点:多处理器退化为单处理器;优势:避免大量的加锁解锁操做

不管你启多少个线程,你有多少个cpu, Python在执行一个进程的时候会淡定的在同一时刻只容许一个线程运行。因此,python是没法利用多核CPU实现多线程的。这样,python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),可是,对于IO密集型的任务效率仍是有显著提高的。

相关文章
相关标签/搜索