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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

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

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

目錄

前言

協程概念

示例代碼

Python編寫協程程序

要用到的庫函數

嘗試編寫異步

嘗試改進函數

嘗試優化代碼

在爬蟲領域的應用?

總結


前言

本節我們介紹一個新概念:協程。協程顧名思義,是協助執行程序的過程。我們將介紹協程的概念和其在Python中的應用。


協程概念

百度百科(協程):

協程不是進程或線程,其執行過程更類似于子例程,或者說不帶返回值的函數調用。

一個程序可以包含多個協程,可以對比與一個進程包含多個線程,因而下面我們來比較協程和線程。我們知道多個線程相對獨立,有自己的上下文,切換受系統控制;而協程也相對獨立,有自己的上下文,但是其切換由自己控制,由當前協程切換到其他協程由當前協程來控制。

注意,協程是應用程序內部切換上下文,不會進入內核進行線程上下文切換。

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


示例代碼

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

Python編寫協程程序

要用到的庫函數

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

要編寫異步程序,要用到asyncio這個庫,它是Python自帶的,直接導入就可以。我們寫一個異步函數,它不能在主函數中直接運行,會拋出Error,因為它直接運行返回的是一個協程對象。我們必須用asyncio的run函數才能運行。

嘗試編寫異步

import asyncio import timeasync def func1():print("你好啊, 我叫李誕")time.sleep(3) # 當程序出現了同步操作的時候. 異步就中斷了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()# 一次性啟動多個任務(協程)asyncio.run(asyncio.wait(tasks)) # 固定搭配t2 = time.time()print(t2 - t1)

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

打印程序執行時間,發現此時執行時間和串行執行的速度差不多——也是9秒多

(打印數據時間+3+2+4)秒。

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

嘗試改進函數

import asyncio import timeasync def func1():print("你好啊, 我叫李誕")# time.sleep(3) # 當程序出現了同步操作的時候. 異步就中斷了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()# 一次性啟動多個任務(協程)asyncio.run(asyncio.wait(tasks))t2 = time.time()print(t2 - t1)

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

但我們這樣并不是最理想化的代碼,我們將其進行優化:

嘗試優化代碼

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掛起操作放在協程對象前面# 第二種寫法(推薦)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()# 一次性啟動多個任務(協程)asyncio.run(main())t2 = time.time()print(t2 - t1)

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

可以看到是沒問題的。


在爬蟲領域的應用?

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

這里用睡眠代替了網絡請求操作,相當于一個模板,以后要批量請求網頁的時候可以套用。

運行結果:


完整代碼

調試請自行修改注釋部分

# import time # # # def func(): # print("我愛黎明") # time.sleep(3) # 讓當前的線程處于阻塞狀態. CPU是不為我工作的 # print("我真的愛黎明") # # # if __name__ == '__main__': # func() # # """ # # input() 程序也是處于阻塞狀態 # # requests.get(bilibili) 在網絡請求返回數據之前, 程序也是處于阻塞狀態的 # # 一般情況下, 當程序處于 IO操作的時候. 線程都會處于阻塞狀態 # # # 協程: 當程序遇見了IO操作的時候. 可以選擇性的切換到其他任務上. # # 在微觀上是一個任務一個任務的進行切換. 切換條件一般就是IO操作 # # 在宏觀上,我們能看到的其實是多個任務一起在執行 # # 多任務異步操作 # # # 上方所講的一切. 都是在單線程的條件下 # """# python編寫協程的程序 import asyncio import time# async def func(): # print("你好啊, 我叫賽利亞") # # # if __name__ == '__main__': # g = func() # 此時的函數是異步協程函數. 此時函數執行得到的是一個協程對象 # # print(g) # asyncio.run(g) # 協程程序運行需要asyncio模塊的支持# async def func1(): # print("你好啊, 我叫李誕") # # time.sleep(3) # 當程序出現了同步操作的時候. 異步就中斷了 # 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() # # 一次性啟動多個任務(協程) # 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掛起操作放在協程對象前面 # # 第二種寫法(推薦) # 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() # # 一次性啟動多個任務(協程) # asyncio.run(main()) # t2 = time.time() # print(t2 - t1)# 在爬蟲領域的應用 async def download(url):print("準備開始下載")await asyncio.sleep(2) # 網絡請求 requests.get()print("下載完成")async def main():urls = ["http://www.baidu.com","http://www.bilibili.com","http://www.163.com"]# 準備異步協程對象列表tasks = []for url in urls:d = asyncio.create_task(download(url))tasks.append(d)# tasks = [asyncio.create_task(download(url)) for url in urls] # 這么干也行哦~# 一次性把所有任務都執行await asyncio.wait(tasks)if __name__ == '__main__':asyncio.run(main())

總結

我們今天認識了協程和異步爬蟲,一步步逐步認識了異步的優點,進一步提高了我們的程序效率。

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。