Python并发编程:
- 1.进程切换需要的资源很最大,效率很低
- 2.线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
- 3.协程切换任务资源很小,效率高(协程本身并不存在,是程序员通过控制IO操作完成)
- 4.多进程、多线程根据CPU核数不一样可能是真实并行的,但是协程是在一个线程中不可能是真正并行的,只能说是并发的
进程:
是系统资源分配的基本单位,也是调度运行的基本单位。例如,用户运行自己的程序,系统就创建了一个进程,并为它分配资源。
进程拥有自己独立的内存空间,所以多个进程间数据不共享,开销大。
线程:
是进程中执行运算的最小单位,如果把进程理解为在逻辑上操作系统所完成的任务,那么线程就表示完成该任务的许多可能的子任务之一
一个进程至少有一个线程,叫主线程,而多个线程共享进程的内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
协程:
是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
多进程:
Python由于cpython解释器的原因,似乎对多进程并不支持,但是可以通过multiprocessing调用多个解释器来实现多进程在Linux系统下,
使用os.fork(),调用一次,返回两次,操作系统自动把当前进程(父进程)复制了一份(子进程),然后分别在父进程和子进程内返回。
子进程永远返回0,父进程返回子进程的ID。经过这样做,父进程就能fork出很多子进程,并可以记录下子进程的ID号了,子进程可以通过getppid()来获取父进程ID。
fork()仅在Unix/Linux下使用,windows则不行。 所以,在Python中,存在一个跨平台的包mutiprocessing,通过引入包中的Process类,就可以创建多进程程序了,可以创建一个进程p=Process(target=func,args=(*,)),然后利用p.start()及p.join()来执行了。以上的join()方法可以等待子进程结束后才往下执行,通常用于进程间同步。 另外,可以用进程池的方式,例如p=Pool(n),然后p.apply_async(func,args),这里可以使用n种不同的参数传入,建立不同的进程。用这种方式时,在调用join()方法前,要先调用close()方法,使得不能再添加新进程。 mutiprocessing包里提供了Queue、Pipe等多种进程间通信的方法。可以直接引入Queue类,然后实例化一个对象。则不同的进程可以使用put方法发信息,同时可以使用get方法取信息。
多线程:
允许一个进程内存在多个控制权,以便让多个函数同时处于激活状态,从而让多个函数的操作同时运行。即使是单CPU的计算机,也可以通过不停地在不同线程的指令间切换,从而造成多线程同时运行的效果。
协程:
又称微线程,纤程。协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行
多进程优缺点:
优点是稳定性好,一个子进程崩溃并不会影响其他子进程和主进程的运行,
缺点是创建进程的代价大,因为操作系统要给每个进程分配固定的资源,造成资源消耗较大,
并且,操作系统对进程的总数会有一定的限制,若进程过多,操作系统调度都会存在问题,会造成假死状态,
所以不能一次性启动太多进程,否则会严重影响系统的资源调度,特别是CPU使用率和负载
多线程优缺点:
同一进程下的多个线程共享这个进程的资源,线程之间切换快,资源消耗较低,也正因为共享进程资源,造成以下缺点:
(1)一个线程挂掉则会影响到共享资源的所有线程,影响到进程,所以不够稳定
(2)当各个线程访问同一数据资源时会出现竞争状态,数据几乎同步会被多个线程占用,造成数据混乱,即 线程不安全
(3)可以通过加线程锁解决(2)中的多线程竞争问题,操作数据时加锁,操作完数据要记得释放锁,以避免死锁,
但是加锁其实阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
协程优缺点:
执行效率极高,因为子程序切换(函数)不是线程切换,由程序自身控制,没有切换线程的开销。
所以与多线程相比,线程的数量越多,协程性能的优势越明显。
不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在控制共享资源时也不需要加锁,因此执行效率高很多。
优点:
1.无需线程上下文切换的开销
2.无需原子操作锁定及同步的开销
3.方便切换控制流,简化编程模型
4.高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
缺点:
1.无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在CPU多核上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
2.进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
区别与联系:
一个进程可以有多个线程,一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能同时使用CPU的多核
都是并发操作,多线程同一时间点只能有一个线程在执行,协程同一时间点只能有一个任务在执行
多线程,是在I/O阻塞时通过切换线程来达到并发的效果,在什么情况下做线程切换是由操作系统来决定的,开发者不用操心,但会造成竞争;
协程,只有一个线程,在I/O阻塞时通过在线程内切换任务来达到并发的效果,在什么情况下做任务切换是开发者决定的,
不会有竞争的情况;多线程的线程切换比协程的任务切换开销更大;对于开发者而言,多线程并发的代码比协程并发的更容易书写。
如果是 I/O 密集型,且 I/O 请求比较耗时的话,使用协程。
如果是 I/O 密集型,且 I/O 请求比较快的话,使用多线程。
如果是 计算 密集型,考虑可以使用多核 CPU,使用多进程
因为,
对于计算密集型,多任务势必造成资源浪费。
对于IO密集型,因为IO速度远低于CPU计算速度,
所以使用多任务方式可以大大增大程序运行效率
在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。