Python协程相关整理

协程

笔记整理,记录一下

asyncio是Python 3.4版本后引入的标准库,内置了对异步IO的支持。 asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。

定义协程

import asyncio

@asyncio.coroutine
def aaa():
    print("hello...")

print(asyncio.iscoroutinefunction(aaa))  # 判断函数是否是协程
print(asyncio.iscoroutine(aaa()))  # 判断是否是协程

# 在3.5过后,我们可以使用async修饰将普通函数和生成器函数包装成异步函数和异步生成器。
async def bbb():
    print("hello2...")

print(asyncio.iscoroutinefunction(bbb))  # True
print(asyncio.iscoroutine(bbb()))  # True

上面的协程只是打印了一句hello..., 现在模拟协程去执行io操作

import asyncio

@asyncio.coroutine
def ccc():
    print("hello...")
    asyncio.sleep(3)  # 看成是一个耗时3秒的IO操作,主线程并未等待,而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行

一个协程可以:

* 等待一个 future 结束
* 等待另一个协程(产生一个结果,或引发一个异常)
* 产生一个结果给正在等它的协程
* 引发一个异常给正在等它的协程

注意:

asyncio.sleep 也是一个协程。 可参见 asyncio.sleep 的文档

sleep(delay, result=None, *, loop=None)
Coroutine that completes after a given time (in seconds).

运行协程

调用协程函数,协程并不会开始运行,只是返回一个协程对象。

协程的运行有两种方式:

  1. 在另一个已经运行的协程中用 yield fromawait
  2. 通过 ensure_future 函数计划它的执行

我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行。简单来说,只有 loop 运行了,协程才可能运行。下面先拿到当前线程的 loop ,然后把协程对象交给 loop.run_until_complete

loop = asyncio.get_event_loop()
loop.run_until_complete(aaa())  # 打印 hello...

注意:

run_until_complete 的参数是一个 future,但是我们这里传给它的却是协程对象,之所以能这样,是因为它在内部做了检查,通过 ensure_future 函数把协程对象包装(wrap)成了 future 。下面是在这部分源码:

In [120]: loop.run_until_complete??
Signature: loop.run_until_complete(future)
Source:
    def run_until_complete(self, future):
        """Run until the Future is done.

        If the argument is a coroutine, it is wrapped in a Task.

        WARNING: It would be disastrous to call run_until_complete()
        with the same coroutine twice -- it would wrap it in two
        different Tasks and that can't be good.

        Return the Future's result, or raise its exception.
        """
        self._check_closed()

        new_task = not futures.isfuture(future)
        future = tasks.ensure_future(future, loop=self)
        if new_task:
            # An exception is raised if the future didn't complete, so there
            # is no need to log the "destroy pending task" message
            future._log_destroy_pending = False

        future.add_done_callback(_run_until_complete_cb)

我们也可以这样写:

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.ensure_future(aaa()))  # 打印 hello...

协程去执行另一个协程

@asyncio.coroutine
def aaa():
    print("hello...")
    yield from bbb()
    # await bbb()  # 同上, 但必须要这样定义协程async def aaa(): pass 否则报语法错误

async def bbb():  # 同aaa
    print("hello2...")

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.ensure_future(aaa()))

# 打印结果
hello...
hello2...
[Finished in 0.5s]

关于执行顺序:

import asyncio

async def aaa():
    print("aaaa")
    r = await bbb("send content")
    print(r)
    print("aaa end")

async def bbb(c):
    print("bbbb")
    print(c)
    return 'response content'
    
def main():
    print("start")
    loop = asyncio.get_event_loop()
    loop.run_until_complete(aaa())
    print("End")

if __name__ == '__main__':
    main()

# 打印结果
start
aaaa
bbbb
send content
response content
aaa end
End
[Finished in 0.5s]

添加回调

可以通过给future添加回调方法,以便执行完Io操作, 希望得到通知

async def aaa():
    print("hello...")
    await bbb()

def cb(fu):
    print("done")
    print(fu)

loop = asyncio.get_event_loop()

fu = asyncio.ensure_future(aaa())
fu.add_done_callback(cb)  # 添加回调方法
loop.run_until_complete(fu)

多个协程

async def aaa():
    print("hello...")
    await asyncio.sleep(3)
    print("hello again...")

async def bbb():
    print("hello2...")
    await asyncio.sleep(3)
    print("hello2 again...")

loop = asyncio.get_event_loop()
# 法一:
fu = asyncio.gather(aaa(), bbb()) 
# 法二:
tasks = [aaa(), bbb()]
fu = asyncio.gather(*tasks)
# 法三:
tasks = [asyncio.ensure_future(aaa()),
             asyncio.ensure_future(bbb())]
fu = asyncio.gather(*tasks)  # gather方法,把多个 futures 包装成单个 future

loop.run_until_complete(fu)  # 两个协程是并发运行的
loop.close()

注意:

asyncio.wait() 也可以将多个协程聚合成一个future

具体差别可请参见 StackOverflow 的讨论:Asyncio.gather vs asyncio.wait

参考文档:
https://segmentfault.com/a/1190000008814676
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432090954004980bd351f2cd4cc18c9e6c06d855c498000
https://zhuanlan.zhihu.com/p/27258289

猜你喜欢

转载自blog.csdn.net/u011361138/article/details/85338442