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------线程

评论关闭