一、概述:
1.異步編程是一種並發編程的模式,其關注點是通過調度不同任務之間的執行和等待時間,通過減少處理器的閑置時間來達到減少整個程序的執行時間;
2.異步編程跟同步編程模型最大的不同就是其任務的切換,當遇到一個需要等待長時間執行的任務的時候,我們可以切換到其他的任務執行;
3.與多線程和多進程編程模型相比,異步編程只是在同一個線程之內的的任務調度,無法充分利用多核CPU的優勢,所以特別適合IO阻塞性任務;
4.同步是指完成事務的邏輯,先執行第一個事務,如果阻塞了,會一直等待,直到這個事務完成,再執行第二個事務,順序執行異步是和同步相對的,異步是指在處理調用這個事務的之后,不會等待這個事務的處理結果,直接處理第二個事務去了,通過狀態、通知、回調來通知調用者處理結果。多線程和多進程都是通過異步的方式處理事物。
二、異步與同步理解圖
同步:
異步:
理解:從上圖可以看出同步在長時間處理時,程序一直等待中,,異步在長時間處理時,可以切換到別的任務(做別的事情)
三、 同步VS異步:
#!/usr/bin/env python # -*- coding: utf-8 -*- ###__________________________________________同步實現網站訪問________________________________________ import time def visit_url(url, response_time): """ 訪問url """ print("visit: {} - {}".format(int(time.time()),url)) time.sleep(response_time)#同步等待 print("response: {}".format(int(time.time()))) return "訪問{}, 已得到返回結果".format(url) def run_task(): visit_url('http://itest.info', 2) visit_url('http://www.testpub.cn', 3) if __name__ == "__main__": start_time = time.perf_counter()#獲取開始時間 run_task() print("消耗時間:{}秒".format(time.perf_counter() - start_time)) ###___________________________________________異步實現網站訪問________________________________________________ import asyncio import time async def visit_url(url, response_time): """訪問 url""" print("visit: {} - {}".format(int(time.time()),url)) await asyncio.sleep(response_time) print("response: {}".format(int(time.time()))) # async def run_task(): # '''非並發寫法''' # await visit_url('http://itest.info', 2) # await visit_url('http://www.testpub.cn', 3) # # if __name__ == "__main__": # start_time = time.perf_counter() # asyncio.get_event_loop().run_until_complete(run_task()) # print("消耗時間:{}秒".format(time.perf_counter() - start_time)) ''' 運行結果(消耗時間沒有變化): visit: 1649735857 - http://itest.info response: 1649735859 visit: 1649735859 - http://www.testpub.cn response: 1649735862 消耗時間:5.001019820249864秒 備注: 你會發現,兩次運行並無差別。 如果想達到並發的效果,可以通過gather()創建任務。修改run_task() 函數 ''' async def run_task(): '''並發函數的寫法''' url1 = visit_url('http://wangzhen.com', 2) url2 = visit_url('http://www.testpub.cn', 3) await asyncio.gather(url1, url2) #asyncio.gather() 方法將多個異步任務(兩個 url)包裝成一個新的異步任務。 if __name__ == "__main__": start_time = time.perf_counter()#獲取開始時間 asyncio.get_event_loop().run_until_complete(run_task()) print("消耗時間:{}秒".format(time.perf_counter() - start_time))
###################簡單的異步函數調用################### import asyncio async def my_async_function(delay): print("Start task {}".format(delay)) #只阻塞當前函數,這里實際是處理的任務(如IO讀寫操作) await asyncio.sleep(delay)#非必填 print("End task {}".format(delay)) async def main(): tasks = [my_async_function(1), my_async_function(2), my_async_function(3)] await asyncio.wait(tasks) print("所有任務執行完成!") asyncio.run(main())
四、案例代碼:
案例1-async異步函數調用
#!/usr/bin/env python # -*- coding: utf-8 -*- ''' 在函數前加上 async那么這個函數就是異步函數 ''' import asyncio import time async def chen(a, b): await asyncio.sleep(4) #異步等待時間(在這個時間內cpu可以執行其他函數),所以可以縮短程序運行時間 #time.sleep(4)#同步等待時間(程序堵塞不執行,等時間到了才執行) print('111111111111111111111111111111') async def hello1(a, b): print('in 11111',int(time.time()))#獲取當前時間戳 time.sleep(2) #模擬同步請求 print("hello1111消耗前",int(time.time())) await asyncio.sleep(3) # 模擬耗時任務3秒 print("hello1111消耗后",int(time.time())) return a + b async def hello2(a, b): print('in 22222',int(time.time())) time.sleep(2) #模擬同步請求 print("hello2222消耗前",int(time.time())) await asyncio.sleep(6) # 模擬耗時任務2秒 print("hello2222消耗后",int(time.time())) return a - b async def hello3(a, b): print('in 3333',int(time.time())) time.sleep(2) #模擬同步請求 print("hello33333消耗前",int(time.time())) #await asyncio.sleep(4) # 模擬耗時任務4秒(這里是異步耗時等待) await chen(a, b)#調用函數 print("hello33333消耗后",int(time.time())) return a * b async def main(): results = await asyncio.gather(hello1(10, 5), hello2(10, 5), hello3(10, 5))#多任務執行,返回的是結果列表 for result in results: print('執行結果',result) start=int(time.time())#獲取當前時間戳 asyncio.get_event_loop().run_until_complete(main())#一體化執行 (asyncio.get_event_loop()獲取消息循環體,,run_until_complete(main())將協程函數添加到事件循環,並啟動main() ) end=int(time.time())#獲取當前時間戳 print('程序總耗時:{}秒'.format(end-start))
案例2-協程異步函數:
#!/usr/bin/python3 # -*- coding: utf-8 -*- #_____________________________async/await 是python3的新特性,可以進行協程運行。個人將他理解成多線程。實現代碼如下__________________________________________________ import time import asyncio async def SleepTime(ts): print('tasks列表為',ts) if ts == 3: await asyncio.sleep(10) #當ts等於3的時候,讓掛起的時間為10s,其他的按正常掛起,驗證協程時間。 else: await asyncio.sleep(ts) async def main(loop): tasks = [] #這里就有6個函數 for i in range(6): print("第{}次".format(i)) tasks.append(loop.create_task(SleepTime(i))) #相當於開啟了一個線程 print("結束{}".format(i)) print("*********************") await asyncio.wait(tasks) #等待所有的任務完成。 if __name__ == "__main__": # main() print("程序開始") tb = time.time() print(tb) #記錄當前時間 loop = asyncio.get_event_loop()#獲取消息循環體,,創建一個事件loop loop.run_until_complete(main(loop))#將協程函數添加到事件循環,並啟動 loop.close()#關閉事件 print(time.time()-tb) #記錄結束時間 print("程序end")
案例3-異步請求-同個接口請求多次:
import httpx import asyncio import time async def request(client): resp = await client.get('http://httpbin.org/get') result = resp.json() async def main(): async with httpx.AsyncClient() as client: # 100 次調用 task_list = [] for _ in range(100): req = request(client) task = asyncio.create_task(req) task_list.append(task) await asyncio.gather(*task_list) if __name__ == "__main__": #開始 start = time.time() asyncio.run(main()) # 結束 end = time.time() print('異步:發送100次請求,耗時:{}'.format(end - start))
案例4-異步請求-請求不同的接口:
#!/usr/bin/env python # -*- coding: utf-8 -*- import asyncio import aiohttp import time,io,sys ###________________________________________異步調用不同接口____________________________________________________ async def fetch(session, url): print("發送請求", url) async with session.get(url, verify_ssl=False) as response: content = await response.content.read() if '.jpg' in url: with open(file=str(time.time()) + ".jpg", mode="wb") as file_object: file_object.write(content) else: print(content) #print('33333333333333333333333333333333333333333333333333333333333333333333') with open(file=str(time.time()) + ".txt", mode="w") as file_object: file_object.write(str(content)) print("保存成功", url) async def main(): async with aiohttp.ClientSession() as session: url_list = [ "https://www.baidu.com", "https://scpic.chinaz.net/files/pic/pic9/202107/apic34061.jpg", "https://www.jd.com" ] tasks= [asyncio.ensure_future(fetch(session,i)) for i in url_list]#組裝任務列表 await asyncio.wait(tasks)#異步啟動任務 if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main())#創建時間循環並啟動 ###___________________________________________異步調用不同接口_________________________________________________ sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030') #改變標准輸出的默認編碼 async def get(url): session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(limit=64, ssl=False)) response = await session.get(url) res_text=await response.text() print('接口響應內容:',res_text) ###把響應結果寫入文件 with open(file=str(time.time()) + ".txt", mode="w",encoding="UTF-8") as file_object: file_object.write(res_text) await session.close() return response async def request(url): print('當前url:',url) await get(url)#異步調用函數 if __name__ == "__main__": start = time.time()#開始時間戳 bb=['http://www.baidu.com','http://www.taobao.com','http://www.jd.com']#需要執行的url tasks = [asyncio.ensure_future(request(a)) for a in bb]#創建任務列表 loop = asyncio.get_event_loop()#獲取消息循環體,,創建一個事件loop loop.run_until_complete(asyncio.wait(tasks))#將協程函數添加到事件循環,並啟動 end = time.time()#結束時間戳 print( '程序總耗時:',end - start)
相關連接:
https://www.cnblogs.com/yarightok/p/15997908.html ..................................異步函數關鍵字
https://blog.csdn.net/weixin_49346599/article/details/108608812.................python異步接口測試(案例)
https://blog.csdn.net/qq_37674086/article/details/122595748 ....................協程異步場景簡單使用示例
https://blog.csdn.net/weixin_54556126/article/details/122366882 ................python自動化測試中使用異步
https://blog.csdn.net/qq_43380180/article/details/111573642.......................異步 async/await
https://www.cjavapy.com/article/2428/ ..........................................................Python 異步編程 協程(async/await)
https://www.cnblogs.com/bubbleboom/p/14971052.html...............................Python協程和異步IO