中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python中的asyncio庫-asyncio的概念是什么

發布時間:2020-08-03 10:20:50 來源:億速云 閱讀:211 作者:清晨 欄目:編程語言

不懂Python中的asyncio庫-asyncio的概念是什么?其實想解決這個問題也不難,下面讓小編帶著大家一起學習怎么去解決,希望大家閱讀完這篇文章后大所收獲。

核心概念

asyncio里面主要有4個需要關注的基本概念

Eventloop

Eventloop可以說是asyncio應用的核心,是中央總控。Eventloop實例提供了注冊、取消和執行任務和回調的方法。

把一些異步函數(就是任務,Task,一會就會說到)注冊到這個事件循環上,事件循環會循環執行這些函數(但同時只能執行一個),當執行到某個函數時,如果它正在等待I/O返回,事件循環會暫停它的執行去執行其他的函數;當某個函數完成I/O后會恢復,下次循環到它的時候繼續執行。因此,這些異步函數可以協同(Cooperative)運行:這就是事件循環的目標。

Coroutine

協程(Coroutine)本質上是一個函數,特點是在代碼塊中可以將執行權交給其他協程:

? cat coro1.py
import asyncio
async def a():
    print('Suspending a')
    await asyncio.sleep(0)
    print('Resuming a')
async def b():
    print('In b')
async def main():
    await asyncio.gather(a(), b())
if __name__ == '__main__':
    asyncio.run(main())

這里面有4個重要關鍵點:

協程要用async def聲明,Python 3.5時的裝飾器寫法已經過時,我就不列出來了。

asyncio.gather用來并發運行任務,在這里表示協同的執行a和b2個協程

在協程a中,有一句await asyncio.sleep(0),await表示調用協程,sleep 0并不會真的sleep(因為時間為0),但是卻可以把控制權交出去了。

asyncio.run是Python 3.7新加的接口,要不然你得這么寫:

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

好了,我們先運行一下看看:

? python coro1.py
Suspending a
In b
Resuming a

看到了吧,在并發執行中,協程a被掛起又恢復過。

Future

接著說Future,它代表了一個「未來」對象,異步操作結束后會把最終結果設置到這個Future對象上。Future是對協程的封裝,不過日常開發基本是不需要直接用這個底層Future類的。我在這里只是演示一下:

In : def c():
...:     print('Inner C')
...:     return 12
...:
In : future = loop.run_in_executor(None, c)  # 這里沒用await,None 表示默認的 executor
Inner C
In : future  # 雖然c已經執行了,但是狀態還是 pending。
Out: <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/local/lib/python3.7/asyncio/futures.py:348]>
In : future.done()  # 還沒有完成
Out: False
In : for a in dir(future):
...:     if not a.startswith('_'):
...:         print(a)
...:
add_done_callback
cancel
cancelled
done
exception
get_loop
remove_done_callback
result
set_exception
set_result

可以對這個Future實例添加完成后的回調(add_done_callback)、取消任務(cancel)、設置最終結果(set_result)、設置異常(如果有的話,set_exception)等。現在我們讓Future完成:

In : await future
Out: 12
In : future
Out: <Future finished result=12>
In : future.done()
Out: True
In : future.result()
Out: 12

看到了吧,await之后狀態成了finished。這里順便說一下,一個對象怎么樣就可以被await(或者說怎么樣就成了一個awaitable對象)呢?給類實現一個__await__方法,Python版本的Future的實現大概如下:

def __await_(self):
    if not self.done():
        self._asyncio_future_blocking = True
        yield self
    if not self.done():
        raise RuntimeError("await wasn't used with future")
    return self.result()

這樣就可以await future了,那為什么await future后Future的狀態就能改變呢,這是因為用loop.run_in_executor創建的Future注冊了一個回調(通過asyncio.futures.wrap_future,加了一個_call_set_state回調, 有興趣的可以通過延伸閱讀鏈接2找上下文)。

__await__里面的yield self不要奇怪,主要是為了兼容__iter__,給舊的yield from用:

In : future = loop.run_in_executor(None, c)
Inner C
In : future
Out: <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/local/lib/python3.7/asyncio/futures.py:348]>
In : def spam():
...:     yield from future
...:
In : s = spam()
In : next(s)
Out: <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/local/lib/python3.7/asyncio/futures.py:348]>
新的替代yield from的用法await必須在異步函數(用 async def申明)中使用:
In : def spam():
...:     await future
...:
  File "cell_name", line 5
SyntaxError: 'await' outside async function

Task

Eventloop除了支持協程,還支持注冊Future和Task2種類型的對象,那為什么要存在Future和Task這2種類型呢?

先回憶前面的例子,Future是協程的封裝,Future對象提供了很多任務方法(如完成后的回調、取消、設置任務結果等等),但是開發者并不需要直接操作Future這種底層對象,而是用Future的子類Task協同的調度協程以實現并發。

Task非常容易創建和使用:

# 或者用task = loop.create_task(a())
In : task = asyncio.ensure_future(a())
In : task
Out: <Task pending coro=<a() running at /Users/dongwm/mp/2019-05-22/coro1.py:4>>
In : task.done()
Out: False
In : await task
Suspending a
Resuming a
In : task
Out: <Task finished coro=<a() done, defined at /Users/dongwm/mp/2019-05-22/coro1.py:4> result=None>
In : task.done()
Out: True

asyncio并發的正確/錯誤姿勢

在代碼中使用async/await是不是就能發揮asyncio的并發優勢么,其實是不對的,我們先看個例子:

async def a():
    print('Suspending a')
    await asyncio.sleep(3)
    print('Resuming a')
async def b():
    print('Suspending b')
    await asyncio.sleep(1)
    print('Resuming b')
async def s1():
    await a()
    await b()

有2個協程a和b,分別sleep1秒和3秒,如果協程可以并發執行,那么執行時間應該是sleep最大的那個值(3秒),現在它們都在s1協程里面被調用。大家先猜一下s1會運行幾秒?

我們寫個小程序驗證一下:

def show_perf(func):
    print('*' * 20)
    start = time.perf_counter()
    asyncio.run(func())
    print(f'{func.__name__} Cost: {time.perf_counter() - start}')

大家注意我這個時間計數用的方法,沒有用time.time,而是用了Python 3.3新增的time.perf_counter它是現在推薦的用法。我們在IPython里面驗證下:

In : from coro2 import *
In : show_perf(s1)
********************
Suspending a
Resuming a
Suspending b
Resuming b
s1 Cost: 4.009796932999961

看到了吧,4秒!!!,相當于串行的執行了(sleep 3 + 1)。這是錯誤的用法,應該怎么用呢,前面的asyncio.gather就可以:

async def c1():
    await asyncio.gather(a(), b())
In : show_perf(c1)
********************
Suspending a
Suspending b
Resuming b
Resuming a
c1 Cost: 3.002452698999832

看到了吧,3秒!另外一個是asyncio.wait:

async def c2():
    await asyncio.wait([a(), b()])
In : show_perf(c2)
...
c2 Cost: 3.0066957049998564

同樣是3秒。先別著急,gather和wait下篇文章還會繼續對比。還有一個方案就是用asyncio.create_task:

async def c3():
    task1 = asyncio.create_task(a())
    task2 = asyncio.create_task(b())
    await task1
    await task2
async def c4():
    task = asyncio.create_task(b())
    await a()
    await task
In : show_perf(c3)
...
c3 Cost: 3.002332438999929
In : show_perf(c4)
...
c4 Cost: 3.002270970000154

都是3秒。asyncio.create_task相當于把協程封裝成Task。不過大家要注意一個錯誤的用法:

async def s2():
    await asyncio.create_task(a())
    await asyncio.create_task(b())
In : show_perf(s2)
...
s2 Cost: 4.004671427999938

直接await task不會對并發有幫助*。asyncio.create_task是Python 3.7新增的高階API,是推薦的用法,其實你還可以用

asyncio.ensure_future和loop.create_task:
async def c5():
    task = asyncio.ensure_future(b())
    await a()
    await task
async def c6():
    loop = asyncio.get_event_loop()
    task = loop.create_task(b())
    await a()
    await task
In : show_perf(c5)
...
c5 Cost: 3.0033873750003295
In : show_perf(c6)
...
c6 Cost: 3.006120122000084

感謝你能夠認真閱讀完這篇文章,希望小編分享Python中的asyncio庫-asyncio的概念是什么內容對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,遇到問題就找億速云,詳細的解決方法等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

民勤县| 海丰县| 稻城县| 兴山县| 五指山市| 河北省| 呼伦贝尔市| 四子王旗| 汉沽区| 东城区| 革吉县| 祥云县| 长春市| 栾川县| 成安县| 瑞安市| 赫章县| 夏河县| 通道| 谢通门县| 精河县| 泾源县| 简阳市| 峨边| 玛曲县| 大渡口区| 聂拉木县| 花莲县| 新民市| 景东| 县级市| 宜黄县| 九台市| 浪卡子县| 团风县| 密山市| 岚皋县| 尚志市| 堆龙德庆县| 广汉市| 呼伦贝尔市|