Python3.x协程

894 阅读 · 2023年8月24日 11:01 更新 · 作者:clannadhh


Python3.x系列的协程有很多不同的地方,这里介绍下主要的:

1、asyncio

 

  • asyncio是Python3.4引进的标准库,直接内置了对IO的支持,asyncio的操作,需要在coroutine中通过yield from完成。            
import asyncio


@asyncio.coroutine
def get_body(i):
    print(f'start{i}')
    yield from asyncio.sleep(1)
    print(f'end{i}')


loop = asyncio.get_event_loop()
tasks = [get_body(i) for i in range(5)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

输出结果:    

start4
start0
start1
start3
start2
end4
end0
end1
end3
end2

它的效果是和Gevent一样的,遇到IO操作的时候,自动切换上下文。

不同的是,它对tasks的操作:task先把这个5个参数不同的函数全部加载进来,然后执行任务,任务执行是无序的。

@asyncio.coroutine把一个generator标记为coroutine类型,然后把这个coroutine扔到eventloop中执行

yield from 语法让我们方便的调用另一个generator。由于asyncio.sleep()也是一个coroutine,线程不会等待,直接中断执行下一个消息循环。当asyncio.sleep()返回时,线程可以从yield from拿到返回值(此处是None),然后接着执行下一行语句。

2、async/await

 

在Python3.5的时候,asyncio添加了两个关键字aysncawait,让coroutine语法更简洁。

async关键字可以将一个函数修饰为协程对象,await关键字可以将需要耗时的操作挂起,一般多是IO操作。

它们是针对coroutine的新语法,只需要把@asyncio.coroutine替换为asyncyield from替换为await

import asyncio


async def get_body(i):
    print(f'start{i}')
    await asyncio.sleep(1)
    print(f'end{i}')


loop = asyncio.get_event_loop()
tasks = [get_body(i) for i in range(5)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

运行结果:

start3
start4
start1
start0
start2
end3
end4
end1
end0
end2

Python3.7以后的版本使用asyncio.run即可。此函数总是会创建一个新的事件循环并在结束时关闭之。它应当被用作 asyncio 程序的主入口点,理想情况下应当只被调用一次。

import asyncio
 
async def work(x):  # 通过async关键字定义一个协程
    for _ in range(3):
        print('Work {} is running ..'.format(x))

coroutine_1 = work(1)  # 协程是一个对象,不能直接运行

# 方式一:
loop = asyncio.get_event_loop()  # 创建一个事件循环
result = loop.run_until_complete(coroutine_1)  # 将协程对象加入到事件循环中,并执行
print(result)  # 协程对象并没有返回结果,打印None
# 方式二:
# asyncio.run(coroutine_1)  #创建一个新的事件循环,并以coroutine_1为程序的主入口,执行完毕后关闭事件循环

 

使用asyncio实现的协程的一些特性:

  • 使用async修饰返回的协程对象,不能直接执行,需要添加到事件循环event_loop中执行。
  • 协程主要是用于实现并发操作,其本质在同一时刻,只能执行一个任务,并非多个任务同时展开。
  • 协程中被挂起的操作,一般都是异步操作,否则使用协程没有啥意义,不能提高执行效率。
  • 协程是在一个单线程中实现的,其并发并未涉及到多线程。