python------线程,,一、我们知道无论是创
python------线程,,一、我们知道无论是创
一、我们知道无论是创建多进程还是创建多线程池来解决问题,都要消耗一定的时间来创建进程、创建线程、以及管理他们之间的切换。
基于单线程来实现并发,这样就可以节省创建线程进程所消耗的时间。
二、如何实现在两个函数之间的切换?
def func1(): print(1) yield print(3) yielddef func2(): g = func1() next(g) print(2) next(g) print(4)func2()‘‘‘1234‘‘‘切换1
def consumer(): while True: n = yield print(‘消费了一个包子%s‘%n)def producer(): g = consumer() next(g) for i in range(5): print(‘生产了包子%s‘%i) g.send(i)producer()‘‘‘生产了包子0消费了一个包子0生产了包子1消费了一个包子1生产了包子2消费了一个包子2生产了包子3消费了一个包子3生产了包子4消费了一个包子4‘‘‘切换2
import timedef consumer(): ‘‘‘任务1:接收数据,处理数据‘‘‘ while True: x=yielddef producer(): ‘‘‘任务2:生产数据‘‘‘ g=consumer() next(g) for i in range(10000000): g.send(i) time.sleep(2)start=time.time()producer() #并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行stop=time.time()print(stop-start)yield无法做到遇到io阻塞
对于单线程下,程序中不可避免的会出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能再一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。
三、协程
协程:是单线程下的并发,协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
协程的本质:在但线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。
需要强调的是:
1:python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其它线程运行)。
2:单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(非io操作的切换与效率无关)
对比操作系统控制线程的切换,用户在单线程内控制协程的切换的优缺点:
优点:
1.协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级。
2.单线程内就可以实现并发的效果,最大限度地利用cpu。
缺点:
1.协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程。
2.协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程。
协程的特点:
1.必须在只有一个单线程里实现并发
2.修改共享数据不需加锁
3.用户程序里自己保存多个控制流的上下文栈
4.附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
四、greenlet模块
from greenlet import greenletdef eat(name): print(‘%s eat 1‘ %name) g2.switch(‘haha‘) print(‘%s eat 2‘ %name) g2.switch()def play(name): print(‘%s play 1‘ %name) g1.switch() print(‘%s play 2‘ %name)g1=greenlet(eat)g2=greenlet(play)g1.switch(‘hjm‘)#可以在第一次switch时传入参数,以后都不需要‘‘‘hjm eat 1haha play 1hjm eat 2haha play 2‘‘‘greenlet实现状态切换
import timefrom greenlet import greenlet # 在单线程中切换状态的模块def eat1(): print(‘吃鸡腿1‘) g2.switch() time.sleep(5) print(‘吃鸡翅2‘) g2.switch()def eat2(): print(‘吃饺子1‘) g1.switch() time.sleep(3) print(‘白切鸡‘)g1 = greenlet(eat1)g2 = greenlet(eat2)g1.switch()‘‘‘吃鸡腿1吃饺子1吃鸡翅2白切鸡‘‘‘greenlet实现状态切换2
单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度。
#顺序执行import timedef f1(): res=1 for i in range(100000000): res+=idef f2(): res=1 for i in range(100000000): res*=istart=time.time()f1()f2()stop=time.time()print(‘run time is %s‘ %(stop-start)) # run time is 10.494175910949707#切换from greenlet import greenletimport timedef f1(): res=1 for i in range(100000000): res+=i g2.switch()def f2(): res=1 for i in range(100000000): res*=i g1.switch()start=time.time()g1=greenlet(f1)g2=greenlet(f2)g1.switch()stop=time.time()print(‘run time is %s‘ %(stop-start)) # run time is 63.0725622177124效率对比
greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。
五、gevent模块
python------线程
评论关闭