现在多进程多线程已经是老生常谈了,协程也在最近几年流行起来。python中有协程库gevent,py web框架tornado中也用了gevent封装好的协程。本文主要介绍进程、线程和协程三者之间的区别。
一、概念
1、进程
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
2、线程
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
3、协程
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
二、区别:
1、进程多与线程比较
线程是指进程内的一个执行单元,也是进程内的可调度实体。线程与进程的区别:
1) 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间
2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
3) 线程是处理器调度的基本单位,但进程不是
4) 二者均可并发执行
5) 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
2、协程多与线程进行比较
1) 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。
2) 线程进程都是同步机制,而协程则是异步
3) 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
三、进程和线程、协程在python中的使用的例子
多进程一般使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,当然生产者消费者这种也可以使用。多进程的优势就是一个子进程崩溃并不会影响其他子进程和主进程的运行,但缺点就是不能一次性启动太多进程,会严重影响系统的资源调度,特别是CPU使用率和负载。注:python2的进程池在类中的使用会有问题,需要把类函数定义成全局函数。
1 接触到python多进程的一个典型的例子如下! 2 import multiprocessing 3 4 def f(x): 5 return x*x 6 7 def go(): 8 pool = multiprocessing.Pool(processes=4) 9 #result = pool.apply_async(self.f, [10]) 10 #print result.get(timeout=1) 11 print pool.map(f, range(10)) 12 13 14 if __name__== '__main__' : 15 go() 16 可是,一旦加入了class,程序就显示错误。程序和结果如下: 17 程序: 18 import multiprocessing 19 20 class someClass(object): 21 def __init__(self): 22 pass 23 24 def f(self, x): 25 return x*x 26 27 def go(self): 28 pool = multiprocessing.Pool(processes=4) 29 #result = pool.apply_async(self.f, [10]) 30 #print result.get(timeout=1) 31 print pool.map(self.f, range(10)) 32 33 结果: 34 PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed
1 import multiprocessing 2 3 def func(x): 4 return x*x 5 6 class someClass(object): 7 def __init__(self,func): 8 self.f = func 9 10 def go(self): 11 pool = multiprocessing.Pool(processes=4) 12 #result = pool.apply_async(self.f, [10]) 13 #print result.get(timeout=1) 14 print pool.map(self.f, range(10)) 15 16 a=someClass(func) 17 a.go() 18 19 #===========打印结果============== 20 21 $python f.py 22 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
总结:
我不是太理解map的过程,但是它必须把self对象传递到各个进程中才行,由于map只需要一个参数,self多了出来。我看了《Python标准库》,这里介绍的是它上的做法。
至于为什么报pickle的错,我也不明白。
四、进程和线程、协程在python中的使用
2、多线程一般是使用threading库,完成一些IO密集型并发操作。多线程的优势是切换快,资源消耗低,但一个线程挂掉则会影响到所有线程,所以不够稳定。现实中使用线程池的场景会比较多,具体可参考https://www.cnblogs.com/rianley/p/9076207.html
3、协程一般是使用gevent库,当然这个库用起来比较麻烦,所以使用的并不是很多。相反,协程在tornado的运用就多得多了,使用协程让tornado做到单线程异步,据说还能解决C10K的问题。所以协程使用的地方最多的是在web应用上。
总结一下就是IO密集型一般使用多线程或者多进程,CPU密集型一般使用多进程,强调非阻塞异步并发的一般都是使用协程,当然有时候也是需要多进程线程池结合的,或者是其他组合方式。
先记录 未完结!