[英]Does gevent monkey patched Queue.put yield (context switch)?
import gevent.monkey
gevent.monkey.patch_all()
import gevent
from queue import Queue
import random
import time
def getter(q):
while True:
print('getting')
v = q.get()
print(f'got {v}')
def putter(q):
while True:
print(f'start putting')
v = int(random.random() * 1000)
# `put_nowait` also seems to yield
# q.put(v)
q.put_nowait(v)
print(f'done putting with {v}')
if random.random() < 0.5:
print(f'yield')
time.sleep(0)
queue = Queue()
gevent.spawn(getter, queue)
gevent.spawn(putter, queue)
time.sleep(1000)
如果我使用queue.put
或queue.put_nowait
,我看到了類似的日志
# start putting
# got 25
# getting
# done putting with 535
這是否表明 gevent 每次執行queue.put
都可能會進行上下文切換?
我稍微修改了代碼
flag = True
def getter(q):
while True:
print('getting')
global flag
flag = False
v = q.get()
print(f'got {v}')
def putter(q):
v = 0
while True:
print(f'start putting {v}')
global flag
flag = True
# `put_nowait` also seems to yield
# q.put(v)
q.put_nowait(v)
if not flag:
raise Exception('yield happened')
print(f'done putting with {v}')
v += 1
time.sleep(0)
# If I sleep with non-zero, the above seems to only yield once at the beginning.
# time.sleep(0.000001)
queue = Queue()
def myTracer(event, args):
src, target = args
if event == "switch":
# print("from %s switch to %s" % (src, target))
# Print to stdout like the rest of the code. Otherwise the order of stdout & stderr is not guaranteed.
traceback.print_stack(file=sys.stdout)
elif event == "throw":
print("from %s throw exception to %s" % (src, target))
# greenlet.settrace(myTracer)
gevent.spawn(getter, queue)
putter(queue)
使用修補的 Python 隊列,
sleep(0)
,它可能會進行上下文切換。sleep(0.0000001)
,它在開始時只產生一次。 查看堆棧跟蹤,我發現Queue.put
調用notify
,后者調用lock.acquire(0)
。 然后對其進行修補,使其在gevent/thread.py
調用sleep()
如果我使用gevent.queue.Queue
而不是 Python Queue
,它似乎不會進行上下文切換。
您可以使用帶有回調函數的greenlet.settrace來檢測上下文切換。
將其添加到您的代碼中表明put
和put_nowait
都執行上下文切換。
...
def myTracer(event, args):
src, target = args
if event == "switch":
print("from %s switch to %s" % (src, target))
elif event == "throw":
print("from %s throw exception to %s" % (src, target))
greenlet.settrace(myTracer)
queue = Queue()
gevent.spawn(getter, queue)
gevent.spawn(putter, queue)
time.sleep(5)
你會在標准輸出中看到很多“切換”調試消息:
...
done putting with 106
start putting
from <Greenlet at 0x10418e150: putter(<queue.Queue object at 0x1034d9460>)> switch to <Hub '' at 0x10416a400 select default pending=0 ref=3 thread_ident=0x106d15dc0>
from <Hub '' at 0x10416a400 select default pending=0 ref=1 thread_ident=0x106d15dc0> switch to <Greenlet at 0x10418e150: putter(<queue.Queue object at 0x1034d9460>)>
done putting with 487
start putting
....
編輯/注意:
我正在使用 Python3.8 和 gevent==20.9.0。 測試時,我刪除了整個 if 條件if random.random() ...
,但將標准輸出管道傳輸到文件並搜索getter
上下文切換並getting
兩者都存在於標准輸出中。
我沒有進一步調查,但是如果您查看源代碼,您會發現有一個顯式調用getter.switch(getter)
可能這就是導致上下文切換的原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.