gevent是一個異步I/O框架,當(dāng)遇到I/O操作的時候,會自動切換任務(wù),從而能異步地完成I/O操作
但是在測試的情況下,可以使用sleep函數(shù)來讓gevent進(jìn)行任務(wù)切換。示例如下:
import geventdef test(id): PRint('Test %s is running...' % id) gevent.sleep(0) print('Test %s is done!' % id)gevent.joinall([gevent.spawn(test, i) for i in range(2)])該函數(shù)的執(zhí)行結(jié)果是:
Test 0 is running...Test 1 is running...Test 0 is done!Test 1 is done!可見,sleep函數(shù)能讓gevent切換協(xié)程,進(jìn)行異步操作。
這次我想探究一下sleep函數(shù)的原理。
在了解sleep函數(shù)之前,我們需要了解一下gevent的運(yùn)行
在前面的文章中,我們知道了gevent有個主協(xié)程hub的概念,當(dāng)需要切換協(xié)程的時候,需要先回到hub,然后再由hub去切換。
其實主協(xié)程hub是一個特殊的協(xié)程Greenlet。
當(dāng)gevent運(yùn)行的時候,gevent需要先創(chuàng)建一個主協(xié)程hub,并運(yùn)行hub的run函數(shù)(具體源碼在hub.py/run),比較簡單,核心代碼是loop.run(),這個run函數(shù)是Greenlet類中的run函數(shù),用來切入loop中的子協(xié)程,源碼在greenlet.py/run中。核心就是result = self._run(*self.args, **self.kwargs), _run函數(shù)用來執(zhí)行這個子協(xié)程的任務(wù)
在剛剛的示例代碼中,在sleep處設(shè)置斷點,進(jìn)行跟蹤。
首先,進(jìn)入sleep函數(shù),函數(shù)在hub.py中:
def sleep(seconds=0, ref=True): hub = get_hub() #獲得主協(xié)程hub對象 loop = hub.loop #獲得主循環(huán) if seconds <= 0: waiter = Waiter() loop.run_callback(waiter.switch) #設(shè)置回調(diào)函數(shù)(即下次本協(xié)程執(zhí)行的地點) waiter.get() else: hub.wait(loop.timer(seconds, ref=ref))當(dāng)seconds=0的時候,loop.run_callback(waiter.switch)把當(dāng)前greenlet的switch注冊到loop中,設(shè)置為回調(diào)函數(shù),此時的loop是主協(xié)程hub下的loop。
sleep函數(shù)中最后調(diào)用了waiter.get(),get函數(shù)簡化如下:
def get(self): assert self.greenlet is None, 'This Waiter is already used by %r' % (self.greenlet, ) self.greenlet = getcurrent() try: return self.hub.switch() finally: self.greenlet = Noneself.greenlet = getcurrent(): 把greenlet設(shè)置為當(dāng)前協(xié)程greenletreturn self.hub.switch(): 切換到主線程hub的主循環(huán), 然后主循環(huán)再切換到下一個greenlet協(xié)程
工作流程如圖:
Gevent的工作原理(省略了執(zhí)行完協(xié)程之后的過程)如下:
hub.run()函數(shù),里面主要是執(zhí)行loop.run(),loop中是子協(xié)程,相當(dāng)于執(zhí)行子協(xié)程的run()函數(shù)Greenlet.run()函數(shù)Greenlet.run()函數(shù)中,執(zhí)行到self._run()函數(shù),即執(zhí)行該協(xié)程的任務(wù),本例中為自己定義的test()函數(shù)waiter.get()函數(shù)waiter.get()函數(shù)將調(diào)用self.hub.switch()切回主協(xié)程hubhub.switch()將調(diào)用greenlet.switch()函數(shù):
Waiter.switch()函數(shù)接著上次執(zhí)行的地方執(zhí)行重復(fù)以上的過程,直至所有協(xié)程任務(wù)全部執(zhí)行完畢
新聞熱點
疑難解答