如何解决gevent monkey打补丁的Queue.put是否产生上下文切换? 更新
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)
。然后对其进行修补,以便在sleep()
gevent/thread.py
如果我使用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)
您将在stdout中看到很多“切换”调试消息:
...
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() ...
,但将stdout传递到文件并搜索getter
上下文开关和getting
都存在于stdout中。
我没有进一步调查,但是如果您查看source code,您会发现有一个显式调用getter.switch(getter)
,这可能是导致上下文切换的原因。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。