日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

初识Python asynic异步编程

發(fā)布時間:2024/8/5 综合教程 56 生活家
生活随笔 收集整理的這篇文章主要介紹了 初识Python asynic异步编程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

什么是異步編程?

同步代碼(synchrnous code)我們都很熟悉,就是運(yùn)行完一個步驟再運(yùn)行下一個。要在同步代碼里面實(shí)現(xiàn)"同時"運(yùn)行多個任務(wù),最簡單也是最直觀地方式就是運(yùn)行多個 threads 或者多個 processes。這個層次的『同時運(yùn)行』多個任務(wù),是操作系統(tǒng)協(xié)助完成的。 也就是操作系統(tǒng)的任務(wù)調(diào)度系統(tǒng)來決定什么時候運(yùn)行這個任務(wù),什么時候切換任務(wù),你自己,作為一個應(yīng)用層的程序員,是沒辦法進(jìn)行干預(yù)的。

我相信你也已經(jīng)聽說了什么關(guān)于 thread 和 process 的抱怨:process 太重,thread 又要牽涉到很多頭條的鎖問題。尤其是對于一個 Python 開發(fā)者來說,由于GIL(全局解釋器鎖)的存在,多線程無法真正使用多核,如果你用多線程來運(yùn)行計算型任務(wù),速度會更慢。

異步編程與之不同的是,值使用一個進(jìn)程,不使用 threads,但是也能實(shí)現(xiàn)"同時"運(yùn)行多個任務(wù)(這里的任務(wù)其實(shí)就是函數(shù))。

這些函數(shù)有一個非常 nice 的 feature:必要的可以暫停,把運(yùn)行的權(quán)利交給其他函數(shù)。等到時機(jī)恰當(dāng),又可以恢復(fù)之前的狀態(tài)繼續(xù)運(yùn)行。這聽上去是不是有點(diǎn)像進(jìn)程呢?可以暫停,可以恢復(fù)運(yùn)行。只不過進(jìn)程的調(diào)度是操作系統(tǒng)完成的,這些函數(shù)的調(diào)度是進(jìn)程自己(或者說程序員你自己)完成的。這也就意味著這將省去了很多計算機(jī)的資源,因?yàn)檫M(jìn)程的調(diào)度必然需要大量 syscall,而 syscall 是很昂貴的。

一 定義一個簡單的協(xié)程:

import asyncio
 
async def execute(x):
    print('Number:', x)
    return x
 
coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')
 
loop = asyncio.get_event_loop()
task = loop.create_task(coroutine)
print('Task:', task)
loop.run_until_complete(task)
print('Task:', task)
print('After calling loop')

# print('Task Result:', task.result())  這樣也能查看task執(zhí)行的結(jié)果

運(yùn)行結(jié)果:

Coroutine: <coroutine object execute at 0x10e0f7830>
After calling execute
Task: <Task pending coro=<execute() running at demo.py:4>>
Number: 1
Task: <Task finished coro=<execute() done, defined at demo.py:4> result=1>
After calling loop

我們使用 async 定義了一個 execute() 方法,方法接收一個數(shù)字參數(shù),方法執(zhí)行之后會打印這個數(shù)字。
隨后我們直接調(diào)用了這個方法,然而這個方法并沒有執(zhí)行,而是返回了一個 coroutine 協(xié)程對象。

隨后我們使用 get_event_loop() 方法創(chuàng)建了一個事件循環(huán) loop,并調(diào)用了 loop 對象的 run_until_complete() 方法將協(xié)程注冊到事件循環(huán) loop 中,然后啟動。最后我們才看到了 execute() 方法打印了輸出結(jié)果。
可見,async 定義的方法就會變成一個無法直接執(zhí)行的 coroutine 對象,必須將其注冊到事件循環(huán)中才可以執(zhí)行。

我們也可以不使用task來運(yùn)行,它里面相比 coroutine 對象多了運(yùn)行狀態(tài),比如 running、finished 等,我們可以用這些狀態(tài)來獲取協(xié)程對象的執(zhí)行情況。

將 coroutine 對象傳遞給 run_until_complete() 方法的時候,實(shí)際上它進(jìn)行了一個操作就是將 coroutine 封裝成了 task 對象,如:

importasyncio

asyncdefexecute(x):
print('Number:',x)

coroutine=execute(1)
print('Coroutine:',coroutine)
print('Aftercallingexecute')

loop=asyncio.get_event_loop()
loop.run_until_complete(coroutine)
print('Aftercallingloop')

View Code

查看了源碼,正好可以驗(yàn)證上面這一觀點(diǎn):

run_until_complete()這個方法位于源碼中的base_events.py,函數(shù)有句注釋:
Run until the Future is done.If the argument is a coroutine, it is wrapped in a Task.

二 發(fā)送網(wǎng)絡(luò)請求結(jié)合aiohttp實(shí)現(xiàn)異步:

我們用一個網(wǎng)絡(luò)請求作為示例,這就是一個耗時等待的操作,因?yàn)槲覀冋埱缶W(wǎng)頁之后需要等待頁面響應(yīng)并返回結(jié)果。耗時等待的操作一般都是 IO 操作,比如文件讀取、網(wǎng)絡(luò)請求等等。協(xié)程對于處理這種操作是有很大優(yōu)勢的,當(dāng)遇到需要等待的情況的時候,程序可以暫時掛起,轉(zhuǎn)而去執(zhí)行其他的操作,從而避免一直等待一個程序而耗費(fèi)過多的時間,充分利用資源。為了測試,我自己先通過flask 創(chuàng)建一個實(shí)驗(yàn)環(huán)境:

from flask import Flask
import time
 
app = Flask(__name__)
 
@app.route('/')
def index():
    time.sleep(3)
    return 'Hello!'
 
if __name__ == '__main__':
    app.run(threaded=True)

開始測試...

import asyncio
import aiohttp
import time
 
start = time.time()
 
async def get(url):
    session = aiohttp.ClientSession()
    response = await session.get(url)
    result = await response.text()
    session.close()
    return result
 
async def request():
    url = 'http://127.0.0.1:5000'          # 訪問flask搭建的服務(wù)器(睡眠3秒),模仿IO阻塞
    print('Waiting for', url)
    result = await get(url)
    print('Get response from', url, 'Result:', result)
 
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
 
end = time.time()
print('Cost time:', end - start)

運(yùn)行結(jié)果:

Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Waiting for http://127.0.0.1:5000
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Get response from http://127.0.0.1:5000 Result: Hello!
Cost time: 3.0199508666992188

我們發(fā)現(xiàn)這次請求的耗時由 15 秒變成了 3 秒,耗時直接變成了原來的 1/5。

代碼里面我們使用了 await,后面跟了 get() 方法,在執(zhí)行這五個協(xié)程的時候,如果遇到了 await,那么就會將當(dāng)前協(xié)程掛起,轉(zhuǎn)而去執(zhí)行其他的協(xié)程,直到其他的協(xié)程也掛起或執(zhí)行完畢,再進(jìn)行下一個協(xié)程的執(zhí)行。

二 總結(jié)

協(xié)程"同時"運(yùn)行多個任務(wù)的基礎(chǔ)是函數(shù)可以暫停(await實(shí)際就是用到了yield)。上面的代碼中使用到了 asyncio的 event_loop,它做的事情,本質(zhì)上來說就是當(dāng)函數(shù)暫停時,切換到下一個任務(wù),當(dāng)時機(jī)恰當(dāng)(這個例子中是請求完成了)恢復(fù)函數(shù)讓他繼續(xù)運(yùn)行(這有點(diǎn)像操作系統(tǒng)了)。

總結(jié)

以上是生活随笔為你收集整理的初识Python asynic异步编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。