一、python協程關鍵字
async: 宣告協程
注意:通過async宣告的不是函式,單獨呼叫不會執行
await: 用來等待可等待的物件,包括協程(就是async宣告的協程)、任務(asyncio.create_task()、asyncio.ensure_future()建立的任務)和Future(是一個低層級的可等待物件,表示一個非同步操作的 最終結果,一般不要自己建立)
注意:當前協程會再await處等待阻塞(回去執行其他協程),知道等待的協程或任務完成
import asyncio
async def main(input):
print('hello')
await asyncio.sleep(1)
print('world')
asyncio.run(main())
二、可等待物件
可等待 物件有三種主要型別: 協程, 任務 和 Future,可以用關鍵字await等待事件完成
協程
- 協程函式: 定義形式為 async def 的函式;
- 協程物件: 呼叫 協程函式 所返回的物件。
任務
- 通過asyncio.create_task()將協程函式建立任務,該協程將自動排入日程準備立即執行
注意: python >= 3.7 - python3.6 使用asyncio.ensure_future()建立任務
Future
- 是一種特殊的 低層級 可等待物件,表示一個非同步操作的 最終結果。
- 當一個 Future 物件 被等待,這意味著協程將保持等待直到該 Future 物件在其他地方操作完畢。
- 一般沒有必要在應用層級的程式碼中建立 Future 物件
可以使用一下方法
asyncio.gather(*coros_or_futures, loop=None, return_exceptions=False)
- 如果 coros_or_futures* 中的某個可等待物件為協程,它將自動作為一個任務加入日程
- 如果所有可等待物件都成功完成,結果將是一個由所有返回值聚合而成的列表。結果值的順序與 aws 中可等待物件的順序一致。
- 如果 return_exceptions 為 False (預設),所引發的首個異常會立即傳播給等待 gather() 的任務。aws 序列中的其他可等待物件 不會被取消 並將繼續執行。 如果 return_exceptions 為 True,異常會和成功的結果一樣處理,並聚合至結果列表。
- 如果 gather() 被取消,所有被提交 (尚未完成) 的可等待物件也會 被取消。
如果 aws 序列中的任一 Task 或 Future 物件 被取消,它將被當作引發了 CancelledError 一樣處理 – 在此情況下 gather() 呼叫 不會 被取消。這是為了防止一個已提交的 Task/Future 被取消導致其他 Tasks/Future 也被取消。
asyncio.shield(arg, *, loop=None)
- 保護一個 可等待物件 防止其被 取消。
- arg是一個協程,它將自動作為任務加入日程。
asyncio.wait_for(fut, timeout, *, loop=None)
- 用來等待一個future設定timeout,timeout 可以為 None,也可以為 float 或 int 型數值表示的等待秒數。如果 timeout 為 None,則等待直到完成。
- 如果發生超時,任務將取消並引發 asyncio.TimeoutError.
- 要避免任務 取消,可以加上 shield()。
asyncio.wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED)
import asyncio async def wait1(): print('in 1') await asyncio.sleep(1) return 1 async def wait2(): print('in 2') await asyncio.sleep(2) return 2
async def wait3():
print('in 3')
await asyncio.sleep(3)
return 3
async def main():
done, pending = await asyncio.wait([wait1(), wait2(), wait3()], timeout=2.5)
print(done,'\n', pending)
asyncio.run(main())
結果
in 1
in 3
in 2
{<Task finished coro=<wait1() done, defined at /Users/zhangjintao/Desktop/隨筆/小測試程式碼/多工/協程/3.7/test.py:3> result=1>, <Task finished coro=<wait2() done, defined at /Users/zhangjintao/Desktop/隨筆/小測試程式碼/多工/協程/3.7/test.py:8> result=2>}
{<Task pending coro=<wait3() running at /Users/zhangjintao/Desktop/隨筆/小測試程式碼/多工/協程/3.7/test.py:16> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x10acc2650>()]>>}
- timeout 超時不會引發異常,超時會返回未完成的任務的狀態, done是完成的任務set,pending是未完成的任務set
- 如果timeout不設定,會預設所有完成後返回結果,pending返回的是空set
三、再執行緒池中執行程式碼
loop.run_in_executor`(executor, func, *args)
import asyncio
import concurrent.futures
def blocking_io():
# File operations (such as logging) can block the
# event loop: run them in a thread pool.
with open('/dev/urandom', 'rb') as f:
return f.read(100)
def cpu_bound():
# CPU-bound operations will block the event loop:
# in general it is preferable to run them in a
# process pool.
return sum(i * i for i in range(10 ** 7))
async def main():
loop = asyncio.get_running_loop()
## Options:
# 1. Run in the default loop's executor:
result = await loop.run_in_executor(
None, blocking_io)
print('default thread pool', result)
# 2. Run in a custom thread pool:
with concurrent.futures.ThreadPoolExecutor() as pool:
result = await loop.run_in_executor(
pool, blocking_io)
print('custom thread pool', result)
# 3. Run in a custom process pool:
with concurrent.futures.ProcessPoolExecutor() as pool:
result = await loop.run_in_executor(
pool, cpu_bound)
print('custom process pool', result)
asyncio.run(main())
詳見:https://docs.python.org/zh-cn/3.7/library/...
本作品採用《CC 協議》,轉載必須註明作者和本文連結