測試平台系列(96) 如何停止測試任務執行
大家好~我是
米洛
!我正在從0到1打造一個開源的介面測試平台, 也在編寫一套與之對應的
教程
,希望大家多多支援。歡迎關注我的公眾號
米洛的測開日記
,獲取最新文章教程!
回顧
上一節我們的前置條件支援了python腳本
類型,讓我們能夠更靈活處理數據了。
今天我們就玩點好玩的,和大家一起探討:怎麼停止一段python程式碼。之後我們將會運用到pity之中。
知識要點
本文需要大家對asyncio
相關知識有一些了解,至於原理方面,大家可以自行查閱,因為我也沒細看。
為什麼要做這個?
針對測試任務
執行非常久的時候,看起來會是阻塞的情況,舉個例子:
某個python腳本裡面寫了time.sleep(1000),導致你的case一直好像沒有執行完成,那我們想結束它,該咋整呢?
今天部落客要聊的就是這方面的內容。
先看看”難題”
我們來寫一個簡單的demo。
- 我們先定義一個非同步無限循環的方法,讓它每隔一秒就列印一行內容
import asyncio
async def run():
while True:
print("still alive")
await asyncio.sleep(1)
試想一下,如果這個方法開啟了,不結束它,它是不是會一直列印下去
?沒錯,我們先試試直接運行之:
很顯然,它是絕對不會停止的,除非你關閉這個py程式。
我們把它想像成
同步方法,是不是也會遇到這個困難:
import time
def run():
time.sleep(10000)
if __name__ == "__main__":
run()
print("done")
可以發現run一旦開始,它就阻塞
了整個程式,下面的done必須要等run結束了才能列印出來。
怎麼停止run
我們都知道,在python3.4以後新增了非同步編程相關的概念,最初是由@coroutine這樣的裝飾器放到方法上,把方法標註為非同步方法
,後面直接從語言層面支援了非同步方法定義(async),那其實裡面還有很多我們不太常用的部分,比如今天要說的create_task。
- 我們改寫下方法
import asyncio
async def run():
while True:
print("still alive")
await asyncio.sleep(1)
async def main():
await asyncio.create_task(run())
if __name__ == "__main__":
asyncio.run(main())
這次我們包了一層方法,利用asyncio.create_task
來創建非同步任務,create_task接收一個coroutine並執行。
執行之後,可以發現這個和上面的情況,任務也會一直進行下去。
我們繼續下一步改造:
我們去掉await
,可以看到run方法確實執行了,但是still alive只列印了一次就結束了。
我對它的理解是,雖然create_task創建了一個非同步任務
,但沒說要await,也就是說沒有說要等它結束。
想像一下這些非同步任務都由一個事件循環控制,當你執行main(main本身也是一個非同步任務)的時候,asyncio.run默認是要執行到main方法
執行完畢的,也就是說,現在事件循環等待main方法執行完畢,main方法裡面又創建了一個非同步任務,但沒有強調需要該任務完成,創建完畢後,由於main任務已經完成了,就導致整個事件結束了。
梳理一下:
-
有await
執行緒開啟 -> 執行main -> main裡面創建非同步任務run -> 等待非同步任務run(一直等一直等)
由於主執行緒沒有結束,所以整個python程式一直在等待非同步任務執行完畢,畢竟它是死循環,所以會一直等下去。
-
無await
執行緒開啟 -> 執行main -> main裡面創建非同步任務run -> 不等待非同步任務run -> main方法結束 -> 執行緒結束 -> 程式退出
以上都是個人結合go的goroutine給出的理解。肯定會有一些差別的地方。
-
再次改造
其實create_task會返回一個task對象,裡面有done和cancel方法,也就是說咱們可以取消他也可以完成他。
import asyncio
async def run():
while True:
print("still alive")
await asyncio.sleep(1)
async def main():
task = asyncio.create_task(run())
await asyncio.sleep(2)
# 2秒後,就停掉這個任務
task.cancel()
print("task任務結束了,main也即將完成,整個程式即將退出")
if __name__ == "__main__":
asyncio.run(main())
一旦調用了create_task,那麼任務就已經開始了
,接著我們用await讓main等待2秒,再調用task.cancel
方法就可以取消這個task,這樣所有事件都結束,程式也會退出了。
看看gif:
這時候有的同學可能會問了,await2秒以後,不管你是否調用cancel,因為沒有await task,所以程式照樣會退出啊,沒法證明task真的被cancel了。
仔細想想,確實說的有道理。那我們再來改造下:
可以看到,這個task死之前還做了垂死掙扎
!!!
- 加上await
可以看到加上await之後,它還是不會停止
。
那我們加上cancel試試:
很遺憾,報錯了~~~不過沒關係,我們繼續改下。只需要加上異常處理,就可以完美實現2秒後自動取消任務
了。
但這個例子依然不是很帥,如果只是想控制
非同步任務的執行時間,那我們可以用wait_for:
import asyncio
async def run():
while True:
print("still alive")
await asyncio.sleep(1)
async def main():
try:
await asyncio.wait_for(run(), 2)
except asyncio.TimeoutError:
print("run方法超過2秒仍未執行完成")
if __name__ == "__main__":
asyncio.run(main())
這樣2秒後,任務還沒結束也不會繼續執行了。
去掉死循環以後,任務會在2秒內完成(因為只等待1秒),這時候就可以看到timeout異常不會被觸發
。
emmmm, 今天的內容就介紹到這了,非同步還有挺多玩法的,我也不是很清楚,大家可以互相交流交流
~~~