当前位置: 首页 > 后端技术 > Python

Python入门基础:理解协程和生成器

时间:2023-03-26 18:23:47 Python

由于GIL的存在,Python多线程性能比单线程还要差。GIL:全局解释器锁(英文:GlobalInterpreterLock,简称GIL),是计算机程序设计语言解释器用来同步线程的一种机制,它在任何时候都只允许一个线程执行。[1]即使在多核处理器上,使用GIL的解释器一次也只允许一个线程执行。于是就有了Coroutine这个东西。Coroutine:协程,又称微线程、纤程,英文名Coroutine。协程的作用是在执行函数A时,可以随时中断执行函数B,然后中断继续执行函数A(可以自由切换)。但是这个过程并不是函数调用(没有call语句),整个过程看起来是多线程的,但是只有一个线程在执行协程。由于协程是由程序主动控制的,没有线程切换的开销,所以执行效率极高。非常适合IO密集型任务。如果是CPU密集型,建议使用多进程+协程。在Python3.4之前,官方并没有对协程的支持,有一些第三方库的实现,比如gevent、Tornado。3.4之后内置了asyncio标准库,正式实现了coroutines的特性。Python对协程的支持是通过生成器实现的,生成器是遵循一定规则的生成器。因此,在了解协程之前,我们必须先了解一下生成器。Generators这里主要讨论yield和yieldfrom这两个表达式,这两个表达式与协程的实现密切相关。yield表达式是在Python2.5中引入的,参见PEP342https://www.python.org/dev/pe...yieldfrom语法是在Python3.3中加入的,参见PEP380https://www.python.org/dev/体育。..方法中包含了yield表达式后,Python会把它当作生成器对象,不再是普通的方法。yield表达式的使用先来看看这个表达式的具体用法:deftest():print("generatorstart")n=1whileTrue:yield_expression_value=yieldnprint("yield_expression_value=%d"%yield_expression_value)n+=1#①创建生成器对象generator=test()print(type(generator))print("\n-------------\n")#②启动生成器next_result=generator.__next__()print("next_result=%d"%next_result)print("\n----------------\n")#③发送值到yieldexpressionsend_result=generator.send(666)print("send_result=%d"%send_result)执行结果:------------generatorstartnext_result=1----------------yield_expression_value=666send_result=2方法说明:__next__()方法:作用是启动或恢复生成器的执行,相当于send(None)send(value)方法:作用是将值发送给yield表达式。启动生成器是对调用send(None)执行结果的描述:①创建生成器对象:包含yield表达式的函数将不再是函数,调用后返回生成器对象②启动生成器:需要在使用生成器__next__或send(None)之前调用它,否则会报错。启动生成器后,代码会执行到yield出现的位置,即yieldn,然后将n传递给generator.__next__()行的返回值。(注意生成器执行到yieldn后会在这里暂停,直到启动下一个生成器)③向yield表达式发送一个值:调用send方法向yield表达式发送一个值,并在此时恢复生成器的执行同一时间。generator从上次中断的位置继续往下执行,遇到下一个yield,generator再次暂停,切换到main函数,打印send_result。理解这个demo的关键是:generator开始或者恢复执行一次,就会在yield处暂停。上面的步骤②只执行了yieldn,并没有执行赋值语句。在步骤③中,生成器在分配yield_expression_value之前恢复执行。在上面生产者和消费者模型的例子中,代码中断-->切换执行体现了协程的一些特点。再举一个生产者和消费者的例子。这个例子来自廖雪峰的Python教程:传统的生产者消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待。小心,死锁是可能的。现在切换到协程。producer生产完消息后,直接通过yield跳转到consumer开始执行。消费者执行完后,切换回生产者继续生产,效率极高。defconsumer():print("[CONSUMER]start")r='start'whileTrue:n=yieldrifnotn:print("nisempty")continueprint("[CONSUMER]消费者正在消费%s"%n)r="200ok"defproducer(c):#启动生成器start_value=c.send(None)print(start_value)n=0whilen<3:n+=1print("[PRODUCER]Producerisproducing%d"%n)r=c.send(n)print('[PRODUCER]Consumerreturn:%s'%r)#关闭generatorc.close()#创建生成器c=consumer()#传入generatorproducer(c)执行结果:[CONSUMER]startstart[PRODUCER]producerisproducing1[CONSUMER]consumerisconsuming1[PRODUCER]Consumerreturn:200ok[PRODUCER]producerisproducing2[CONSUMER]consumerisconsuming2[PRODUCER]Consumerreturn:200ok[PRODUCER]producerisproducing3[CONSUMER]consumerisconsuming3[PRODUCER]Consumerreturn:200ok注意消费者函数是一个generator,把一个consumer传入produce后:首先调用c.森d(None)启动发电机;然后,一旦产生了一些东西,通过c.send(n)切换到消费者执行;consumer通过yield获取消息,进行处理,通过yield返回结果;produce获取消费者处理结果,继续生产下一条消息;produce决定不生产,通过c.close()关闭consumer。整个过程无锁结束,由一个线程执行。生产者和消费者协作完成任务,所以称为“协程”,而不是线程的抢占式多任务。yieldfrom表达式Python3.3版本增加了yieldfrom语法,用于将一个生成器的一些操作委托给另一个生成器。此外,允许子生成器(即yieldfrom之后的“参数”)返回一个可以被委托生成器(即包含yieldfrom的生成器)使用的值。在委托生成器中,可以优化子生成器。我们先来看最简单的应用,例如:#Subgeneratordeftest(n):i=0whilei