浅谈Coroutine使用方法

1.概述

了解携程可以看一下携程和任务
那什么是携程,先从官方的解释入手看一下什么事Coroutines

Coroutines are a more generalized form of subroutines. Subroutines are entered at one point and exited at another point. Coroutines can be entered, exited, and resumed at many different points. They can be implemented with the async def statement. See also PEP 492 .

Glossary — Python 3.8.2 documentation

Coroutine是一个可以暂停执行将执行权让给其他Coroutine或者Awaitables obj的函数,等待执行完成后继续执行,并可以多次进行这样的暂停与继续。功能有点像cpu任务调度的时间片的概念,并发执行任务时,cpu调用给每一个任务分配了一定的时间片,时间片到了之后挂起当前的线程转给其他线程使用。

Asyncio is a library to write concurrent code using the async/await
syntax. asyncio is used as a foundation for multiple Python
asynchronous frameworks that provide high-performance network and
web-servers, database connection libraries, distributed task queues,
etc.

asyncio — Asynchronous I/O — Python 3.8.2 documentation

那如果要判斷一個函數是不是 Coroutine?

  • 可以使用 asyncio.iscoroutinefunction(func) 方法判別。
  • 如果要判斷一個函數返回的是不是 Coroutine 對象? 可以使用 asyncio.iscoroutine(obj) 方法判別。

2.asyncio使用

环境要求:

  1. Python 3.5+ 開始可以使用 asyncio 標準庫和 await syntax 語法糖來寫 Coroutines:PEP 492 — Coroutines with async and await syntax | Python.org
  2. Python 3.7+ 開始可以使用 asyncio.run( ):cpython/runners.py at 3.8 · python/cpython · GitHub
# @File : demo1.py
import asyncio
import time

now = lambda : time.time()

async def dosomething(num):
    print(f'第{num}任务,第一步')
    await asyncio.sleep(2)
    print(f'第{num}任务,第二步')

if __name__ == '__main__':
    start = now()
    tasks = [dosomething(i) for i in range(5)]
    asyncio.run(asyncio.wait(tasks))
    print('Time: ', now() - start)

输出结果如下:

4任务,第一步
第1任务,第一步
第3任务,第一步
第0任务,第一步
第2任务,第一步
第4任务,第二步
第1任务,第二步
第3任务,第二步
第0任务,第二步
第2任务,第二步
Time:  2.003861904144287

如果只是使用普通的函数调用,在函数dosomething中sleep(2),则运行完成需要耗时10s+

2.1 async / await

async / await是 Python3.5+之后出现的语法糖,让Coroutine和Coroutine之间的调度更加清晰。总体来说:

  • async: 用来告知function有异步的功能
  • await:用来标记Coroutine切换暂停和继续的点,类似一个tag

使用 async 来宣告一个 native Coroutine:
注意:async def func 内无法与 yield 或 yield from 共同使用,会引发SyntaxError 错误
使用方法:将 async 加在 function 前面,如下所示:

async def read_data(db):
    pass

使用await让Coroutine挂起:
注意: await 后面必须接一个 Coroutine 对象或是 awaitable 类型的对象
await 的目的是将控制权回传给事件循环 (event loop) 并等待返回,而背后实现暂停挂起函数操作的是 yield
使用方法:加在要等待的 function 前面,如下所示:

async def read_data(db):
    data = await db.fetch('SELECT ...')

那什么事Awaitables特性?
有三种主要类型:coroutines、 Tasks 、Futures

  1. coroutines:一个 async def 函数就是 coroutine,也因为 awaitables 特性所以可以等待其他 coroutine。
  2. tasks:tasks 是用来调度 coroutines,可通过 asyncio.create_task( ) 來打包 coroutines。
  3. futures:futures 是一个异步操作 (asynchronous operation) 返回的結果

2.2 如何建事件循环?

Python 3.5+ 使用 asyncio.get_event_loop 先建立一个 event_loop,然后再将 Coroutine 放进 run_until_complete() 里面,直到所有 Coroutine 运行结束

import asyncio

async def hello_world(x):
    print('hello_world x' + str(x))
    await asyncio.sleep(x)

loop = asyncio.get_event_loop()
loop.run_until_complete(hello_world(3))
loop.close()

Python 3.7推出更简单的方法:
Python 3.7+ 之后将 loop 封裝,只需要使用 asyncio.run() 一行程式就結束,不用在建立 event_loop 结束时也不需要 loop.close,因为他都帮你做完了,有兴趣可以參考:cpython/runners.py at 3.8 · python/cpython · GitHub

import asyncio

async def hello_world(x):
    print('hello_world x' + str(x))
    await asyncio.sleep(x)

asyncio.run(hello_world(2))

2.3 如何建立任务Task

建立任务有两种方法:

  • asyncio.create_task() : Python 3.7+以上可以使用
  • asyncio.ensure_future():可读性较差
# In Python 3.7+
task = asyncio.create_task(main())

# This works in all Python versions but is less readable
task = asyncio.ensure_future(main())

这里给出一个完整的示例:

# @File : demo2.py
import asyncio
import time

async def dosomething(num):
    print('start{}'.format(num))
    await asyncio.sleep(num)
    print('sleep{}'.format(num))

async def main():
    task1 = asyncio.create_task(dosomething(1))
    task2 = asyncio.create_task(dosomething(2))
    task3 = asyncio.create_task(dosomething(3))
    await task1
    await task2 
    await task3

if __name__ == '__main__':
    time_start = time.time()
    asyncio.run(main())
    print(time.time() - time_start)

输出结果如下:

start1
start2
start3
sleep1
sleep2
sleep3
3.000866651535034

2.4 如何同时运行多个Tasks任务

使用 asyncio.gather(),可同时放入多个Coroutinues或者 awaitable object进入事件循环(event loop),等待Coroutinues都结束后,并依续收集其回传的值。
asyncio.gather( *aws, loop=None, return_exceptions=False)

*aws :可传入多个 awaitable objects
Loop:此参数将会在 Python version 3.10 移除
return_exceptions:default 是 False,当发生 exception 时会立即中断 task,如果设定为 True 则发生错误的信息回与其他成功信息一起回传(如下示例,最终的 results 结果里面包含了 ValueError() 結果)

# @File : demo3.py
import asyncio
import time

now = lambda: time.time()

async def dosomething(num):
    print('第 {} 任务,第一步'.format(num))
    await asyncio.sleep(2)
    print('第 {} 任务,第二步'.format(num))
    return '第 {} 任务完成'.format(num)

async def raise_error(num):
    raise ValueError
    print('这里不会被执行')

async def main():
    tasks = [dosomething(i) for i in range(5)]
    tasks1 = [raise_error(i) for i in range(5)]

    results = await asyncio.gather(*tasks, *tasks1, return_exceptions=True)
    print(results)


if __name__ == "__main__":


    start = now()
    asyncio.run(main())
    print('TIME: ', now() - start)

输出结果如下:

0 任务,第一步
第 1 任务,第一步
第 2 任务,第一步
第 3 任务,第一步
第 4 任务,第一步
第 0 任务,第二步
第 1 任务,第二步
第 2 任务,第二步
第 3 任务,第二步
第 4 任务,第二步
['第 0 任务完成', '第 1 任务完成', '第 2 任务完成', '第 3 任务完成', '第 4 任务完成', ValueError(), ValueError(), ValueError(), ValueError(), ValueError()]
TIME:  2.001753091812134

2.5 并发请求示例

从并发编程谈协程
使用asyncio并发get请求
How could I use requests in asyncio?

参考:协程与任务

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Lua 中,协程(coroutine)是一种特殊的线程,可以在执行过程中挂起和恢复。协程可以用于实现协作式多任务,允许程序在某个点上暂停,并在稍后的时间点继续执行。下面是一些常见的 Lua 协程使用方法: 1. 创建协程:使用 `coroutine.create` 函数来创建一个新的协程。 ```lua local co = coroutine.create(function() -- 协程的执行逻辑 end) ``` 2. 启动协程:使用 `coroutine.resume` 函数来启动或恢复一个协程的执行。 ```lua local success, result = coroutine.resume(co) ``` 3. 挂起协程:使用 `coroutine.yield` 函数来手动挂起一个协程的执行,并返回结果。 ```lua local function foo() print("Hello") coroutine.yield() print("World") end local co = coroutine.create(foo) coroutine.resume(co) -- 输出 "Hello" coroutine.resume(co) -- 输出 "World" ``` 4. 检查协程状态:使用 `coroutine.status` 函数来检查协程的状态。 ```lua local status = coroutine.status(co) ``` 常见的状态有: - "suspended":协程处于挂起状态,即已经创建但尚未执行或已经执行但被挂起。 - "running":当前正在执行的协程。 - "dead":协程已经执行完毕或发生错误导致终止。 以上是一些基本的协程使用方法,还可以使用 `coroutine.wrap` 函数将一个函数转换为协程。在实际应用中,协程可以用于实现状态机、异步操作、迭代器等功能。根据具体需求,可以灵活运用协程的特性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值