簡體   English   中英

gevent 猴子是否修補了 Queue.put 產量(上下文切換)?

[英]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.putqueue.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來檢測上下文切換。
將其添加到您的代碼中表明putput_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM