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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

22. 协程与Python中的多任务异步协程

發(fā)布時(shí)間:2023/12/16 python 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 22. 协程与Python中的多任务异步协程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

前言

協(xié)程概念

示例代碼

Python編寫協(xié)程程序

要用到的庫函數(shù)

嘗試編寫異步

嘗試改進(jìn)函數(shù)

嘗試優(yōu)化代碼

在爬蟲領(lǐng)域的應(yīng)用?

總結(jié)


前言

本節(jié)我們介紹一個(gè)新概念:協(xié)程。協(xié)程顧名思義,是協(xié)助執(zhí)行程序的過程。我們將介紹協(xié)程的概念和其在Python中的應(yīng)用。


協(xié)程概念

百度百科(協(xié)程):

協(xié)程不是進(jìn)程或線程,其執(zhí)行過程更類似于子例程,或者說不帶返回值的函數(shù)調(diào)用。

一個(gè)程序可以包含多個(gè)協(xié)程,可以對比與一個(gè)進(jìn)程包含多個(gè)線程,因而下面我們來比較協(xié)程和線程。我們知道多個(gè)線程相對獨(dú)立,有自己的上下文,切換受系統(tǒng)控制;而協(xié)程也相對獨(dú)立,有自己的上下文,但是其切換由自己控制,由當(dāng)前協(xié)程切換到其他協(xié)程由當(dāng)前協(xié)程來控制。

注意,協(xié)程是應(yīng)用程序內(nèi)部切換上下文,不會(huì)進(jìn)入內(nèi)核進(jìn)行線程上下文切換。

協(xié)程主要用于某一進(jìn)程CPU調(diào)用睡眠時(shí)或者進(jìn)行I/O操作時(shí),選擇性地切換到其他任務(wù),從而提高效率。在微觀上是一個(gè)任務(wù)一個(gè)任務(wù)的進(jìn)行切換,切換條件一般就是IO操作。在宏觀上,我們能看到的其實(shí)是多個(gè)任務(wù)一起在執(zhí)行(多任務(wù)異步操作)


示例代碼

import timedef func():print("我愛黎明")time.sleep(3) # 讓當(dāng)前的線程處于阻塞狀態(tài). CPU是不為我工作的print("我真的愛黎明")if __name__ == '__main__':func()""" # input() 程序也是處于阻塞狀態(tài) # requests.get(bilibili) 在網(wǎng)絡(luò)請求返回?cái)?shù)據(jù)之前, 程序也是處于阻塞狀態(tài)的 # 一般情況下, 當(dāng)程序處于 IO操作的時(shí)候. 線程都會(huì)處于阻塞狀態(tài)# 協(xié)程: 當(dāng)程序遇見了IO操作的時(shí)候. 可以選擇性的切換到其他任務(wù)上. # 在微觀上是一個(gè)任務(wù)一個(gè)任務(wù)的進(jìn)行切換. 切換條件一般就是IO操作 # 在宏觀上,我們能看到的其實(shí)是多個(gè)任務(wù)一起在執(zhí)行 # 多任務(wù)異步操作# 上方所講的一切. 都是在單線程的條件下 """

Python編寫協(xié)程程序

要用到的庫函數(shù)

# python編寫協(xié)程的程序 import asyncioasync def func():print("你好啊, 我叫賽利亞")if __name__ == '__main__':g = func() # 此時(shí)的函數(shù)是異步協(xié)程函數(shù). 此時(shí)函數(shù)執(zhí)行得到的是一個(gè)協(xié)程對象# print(g)asyncio.run(g) # 協(xié)程程序運(yùn)行需要asyncio模塊的支持

要編寫異步程序,要用到asyncio這個(gè)庫,它是Python自帶的,直接導(dǎo)入就可以。我們寫一個(gè)異步函數(shù),它不能在主函數(shù)中直接運(yùn)行,會(huì)拋出Error,因?yàn)樗苯舆\(yùn)行返回的是一個(gè)協(xié)程對象。我們必須用asyncio的run函數(shù)才能運(yùn)行。

嘗試編寫異步

import asyncio import timeasync def func1():print("你好啊, 我叫李誕")time.sleep(3) # 當(dāng)程序出現(xiàn)了同步操作的時(shí)候. 異步就中斷了print("你好啊, 我叫李誕")async def func2():print("你好啊, 我叫王建國")time.sleep(2)print("你好啊, 我叫王建國")async def func3():print("你好啊, 我叫李雪琴")time.sleep(4)print("你好啊, 我叫李雪琴")if __name__ == '__main__':f1 = func1()f2 = func2()f3 = func3()tasks = [f1, f2, f3]t1 = time.time()# 一次性啟動(dòng)多個(gè)任務(wù)(協(xié)程)asyncio.run(asyncio.wait(tasks)) # 固定搭配t2 = time.time()print(t2 - t1)

我們想一次性執(zhí)行多個(gè)異步任務(wù)時(shí),需要把它們放在列表中,并且用asyncio的run函數(shù)中嵌套wait函數(shù)才能實(shí)現(xiàn),它是固定搭配,可以套公式。

打印程序執(zhí)行時(shí)間,發(fā)現(xiàn)此時(shí)執(zhí)行時(shí)間和串行執(zhí)行的速度差不多——也是9秒多

(打印數(shù)據(jù)時(shí)間+3+2+4)秒

問題出在time.sleep()。當(dāng)異步函數(shù)中出現(xiàn)同步操作時(shí),異步就中斷了,所以還是在等待睡眠時(shí)間中CPU什么都沒有執(zhí)行。

嘗試改進(jìn)函數(shù)

import asyncio import timeasync def func1():print("你好啊, 我叫李誕")# time.sleep(3) # 當(dāng)程序出現(xiàn)了同步操作的時(shí)候. 異步就中斷了await asyncio.sleep(3) # 異步操作的代碼print("你好啊, 我叫李誕")async def func2():print("你好啊, 我叫王建國")# time.sleep(2)await asyncio.sleep(2)print("你好啊, 我叫王建國")async def func3():print("你好啊, 我叫李雪琴")# time.sleep(4)await asyncio.sleep(4)print("你好啊, 我叫李雪琴")if __name__ == '__main__':f1 = func1()f2 = func2()f3 = func3()tasks = [f1, f2, f3]t1 = time.time()# 一次性啟動(dòng)多個(gè)任務(wù)(協(xié)程)asyncio.run(asyncio.wait(tasks))t2 = time.time()print(t2 - t1)

我們將睡眠操作改為異步,嘗試執(zhí)行,打印執(zhí)行時(shí)間為4秒多(最長的睡眠時(shí)間+調(diào)度時(shí)間)

但我們這樣并不是最理想化的代碼,我們將其進(jìn)行優(yōu)化:

嘗試優(yōu)化代碼

import time import asyncioasync def func1():print("你好啊, 我叫李誕")await asyncio.sleep(3)print("你好啊, 我叫李誕")async def func2():print("你好啊, 我叫王建國")await asyncio.sleep(2)print("你好啊, 我叫王建國")async def func3():print("你好啊, 我叫李雪琴")await asyncio.sleep(4)print("你好啊, 我叫李雪琴")async def main():# 第一種寫法# f1 = func1()# await f1 # 一般await掛起操作放在協(xié)程對象前面# 第二種寫法(推薦)tasks = [asyncio.create_task(func1()), # py3.8以后加上asyncio.create_task()asyncio.create_task(func2()),asyncio.create_task(func3())]await asyncio.wait(tasks)if __name__ == '__main__':t1 = time.time()# 一次性啟動(dòng)多個(gè)任務(wù)(協(xié)程)asyncio.run(main())t2 = time.time()print(t2 - t1)

這里還是推薦把函數(shù)放在tasks列表中,然后在main異步函數(shù)中異步執(zhí)行異步函數(shù)列表,然后在主程序中調(diào)用main異步函數(shù)。我們依舊輸出運(yùn)行時(shí)間,查看是否成功異步運(yùn)行:

可以看到是沒問題的。


在爬蟲領(lǐng)域的應(yīng)用?

import asyncio# 在爬蟲領(lǐng)域的應(yīng)用 async def download(url):print("準(zhǔn)備開始下載")await asyncio.sleep(2) # 網(wǎng)絡(luò)請求 requests.get()print("下載完成")async def main():urls = ["http://www.baidu.com","http://www.bilibili.com","http://www.163.com"]# 準(zhǔn)備異步協(xié)程對象列表tasks = []for url in urls:d = asyncio.create_task(download(url))tasks.append(d)# tasks = [asyncio.create_task(download(url)) for url in urls] # 這么干也行哦~# 一次性把所有任務(wù)都執(zhí)行await asyncio.wait(tasks)if __name__ == '__main__':asyncio.run(main())

這里用睡眠代替了網(wǎng)絡(luò)請求操作,相當(dāng)于一個(gè)模板,以后要批量請求網(wǎng)頁的時(shí)候可以套用。

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


完整代碼

調(diào)試請自行修改注釋部分

# import time # # # def func(): # print("我愛黎明") # time.sleep(3) # 讓當(dāng)前的線程處于阻塞狀態(tài). CPU是不為我工作的 # print("我真的愛黎明") # # # if __name__ == '__main__': # func() # # """ # # input() 程序也是處于阻塞狀態(tài) # # requests.get(bilibili) 在網(wǎng)絡(luò)請求返回?cái)?shù)據(jù)之前, 程序也是處于阻塞狀態(tài)的 # # 一般情況下, 當(dāng)程序處于 IO操作的時(shí)候. 線程都會(huì)處于阻塞狀態(tài) # # # 協(xié)程: 當(dāng)程序遇見了IO操作的時(shí)候. 可以選擇性的切換到其他任務(wù)上. # # 在微觀上是一個(gè)任務(wù)一個(gè)任務(wù)的進(jìn)行切換. 切換條件一般就是IO操作 # # 在宏觀上,我們能看到的其實(shí)是多個(gè)任務(wù)一起在執(zhí)行 # # 多任務(wù)異步操作 # # # 上方所講的一切. 都是在單線程的條件下 # """# python編寫協(xié)程的程序 import asyncio import time# async def func(): # print("你好啊, 我叫賽利亞") # # # if __name__ == '__main__': # g = func() # 此時(shí)的函數(shù)是異步協(xié)程函數(shù). 此時(shí)函數(shù)執(zhí)行得到的是一個(gè)協(xié)程對象 # # print(g) # asyncio.run(g) # 協(xié)程程序運(yùn)行需要asyncio模塊的支持# async def func1(): # print("你好啊, 我叫李誕") # # time.sleep(3) # 當(dāng)程序出現(xiàn)了同步操作的時(shí)候. 異步就中斷了 # await asyncio.sleep(3) # 異步操作的代碼 # print("你好啊, 我叫李誕") # # # async def func2(): # print("你好啊, 我叫王建國") # # time.sleep(2) # await asyncio.sleep(2) # print("你好啊, 我叫王建國") # # # async def func3(): # print("你好啊, 我叫李雪琴") # await asyncio.sleep(4) # print("你好啊, 我叫李雪琴") # # # if __name__ == '__main__': # f1 = func1() # f2 = func2() # f3 = func3() # tasks = [ # f1, f2, f3 # ] # t1 = time.time() # # 一次性啟動(dòng)多個(gè)任務(wù)(協(xié)程) # asyncio.run(asyncio.wait(tasks)) # t2 = time.time() # print(t2 - t1)# async def func1(): # print("你好啊, 我叫李誕") # await asyncio.sleep(3) # print("你好啊, 我叫李誕") # # # async def func2(): # print("你好啊, 我叫王建國") # await asyncio.sleep(2) # print("你好啊, 我叫王建國") # # # async def func3(): # print("你好啊, 我叫李雪琴") # await asyncio.sleep(4) # print("你好啊, 我叫李雪琴") # # # async def main(): # # 第一種寫法 # # f1 = func1() # # await f1 # 一般await掛起操作放在協(xié)程對象前面 # # 第二種寫法(推薦) # tasks = [ # asyncio.create_task(func1()), # py3.8以后加上asyncio.create_task() # asyncio.create_task(func2()), # asyncio.create_task(func3()) # ] # await asyncio.wait(tasks) # # # if __name__ == '__main__': # t1 = time.time() # # 一次性啟動(dòng)多個(gè)任務(wù)(協(xié)程) # asyncio.run(main()) # t2 = time.time() # print(t2 - t1)# 在爬蟲領(lǐng)域的應(yīng)用 async def download(url):print("準(zhǔn)備開始下載")await asyncio.sleep(2) # 網(wǎng)絡(luò)請求 requests.get()print("下載完成")async def main():urls = ["http://www.baidu.com","http://www.bilibili.com","http://www.163.com"]# 準(zhǔn)備異步協(xié)程對象列表tasks = []for url in urls:d = asyncio.create_task(download(url))tasks.append(d)# tasks = [asyncio.create_task(download(url)) for url in urls] # 這么干也行哦~# 一次性把所有任務(wù)都執(zhí)行await asyncio.wait(tasks)if __name__ == '__main__':asyncio.run(main())

總結(jié)

我們今天認(rèn)識(shí)了協(xié)程和異步爬蟲,一步步逐步認(rèn)識(shí)了異步的優(yōu)點(diǎn),進(jìn)一步提高了我們的程序效率。

總結(jié)

以上是生活随笔為你收集整理的22. 协程与Python中的多任务异步协程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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