进程、线程和协程的调度和运行原理总结。html
linux的操做系统详细调度策略可参考:http://blog.csdn.net/gatieme/article/details/51872659async
linux中的进程主要有三种调度策略:函数
优先级调度:将进程分为普通进程和实时进程;
先进先出(队列)调度:实时进程先建立的先执行,直到遇到io或主动阻塞。
轮转调度(时间片):达到必定的CPU执行时间后强制切换;
多进程程序的调度其实仍是线程的调度,线程才是CPU调度的基本单位;在同一个进程内线程切换不会产生进程切换,由一个进程内的线程切换到另外一个进程内的线程时,将会引发进程切换。
正在执行的进程执行完毕;
执行中进程发生阻塞;(如调用sleep)
执行中进程调用了P原语操做,从而因资源不足而被阻塞;或调用了v原语操做激活了等待资源的进程队列;
执行中进程提出I/O请求后被阻塞;
CPU分配的时间片用完;(默认10ms)
就绪队列中的某进程的优先级变得高于当前执行进程的优先级,引起强制切换;
若是咱们使用python建立了多进程或多线程,能够认为这几个进程或线程是在公平队列(即优先级相同)的实时进程,那么其调度策略是FIFO和RR。
举个例子,假设如今有一个单核的CPU,python程序建立了5个线程,这五个线程会按建立的时间前后进入到一个公平队列中,CPU按先进先出原则开始执行第一个线程,若是遇到IO操做或休眠,或者执行这个线程的时间超过10ms;CUP就会中止当前线程,切换到第二个线程执行直到第五个线程;而后又从第一个线程开始循环,直到全部的线程执行完毕资源被操做系统回收。
固然,切换进程或线程也须要付出代价的,进程切换的代价大于线程。
进程:
建立一个进程后,每一个进程拥有本身独立的内存地址空间,代码段,数据段,BSS段,堆,栈等全部用户空间的信息;
多进程中,子进程复制主进程的几乎全部信息,除了pid等特殊信息;
线程:
一个进程下多个线程,多个线程共享进程的进程代码段,进程的公有数据(堆),进程的所拥有其余辅助资源;
各个线程独立拥有的资源包括:线程id,程序计数器,一个栈,计数器寄存器和栈用来保存线程的执行历史和执行状态。
协程:
协程能够看作轻量级的线程,即协程是在线程下开启,多协程在单线程下实现并发,而操做系统最多只能感知到线程,也就是说协程的切换对于操做系统来讲是无感知的,属于程序级别的切换;
多个协程共享单线程的代码段、公有数据(堆)等;
每一个协程拥有本身的栈来保存上下文状态,协程的切换开销更小,对操做系统来讲,会认为一个开启了多协程的线程一直在计算;
协程的优点在于切换的代价更小,所以CPU的有效利用率获得了提升。
python的协程主流经过gevent和asyncio模块实现,它们的核心原理都是底层用代码建立事件循环来对多个协程的上下文进行调度;
python的代码尽可能避免使用多线程;
若是上下文有一段代码能够分红相对独立的两个部分,若是独立的两个部分是CPU密集型,那么使用多进程;若是是IO密集型,那么使用协程;若是二者都涉及,能够考虑使用子进程中运行协程。
业务代码为了快速建立协程或进程,同时加强代码的可读性,推荐使用匿名函数。
from redis import StrictRedis rs = StrictRedis(host='192.168.1.20', port=6390, db=1) def get_rs1(): t = time.time() res = gevent.joinall([gevent.spawn(lambda x: gevent.sleep(2), x=i) for i in range(2)]) ls = [x.get() if x.kwargs['x'] == 1 else x.get() for x in res] print(ls) print(time.time() - t) if __name__ == '__main__': get_rs1() # gevent须要判断返回的结果的顺序