首页 > 解决方案 > 如何使gevent sleep精确?

问题描述

我正在用 gevent 开发一个负载测试工具。

我创建了一个如下所示的测试脚本

while True:
    # send http request
    response = client.sendAndRecv()

    gevent.sleep(0.001)

发送/接收动作完成得非常快,比如 0.1 毫秒

所以预期的速率应该接近每秒 1000 次。

但实际上我在 Ubuntu 和 Windows 平台上都得到了大约每秒 500 个。

gevent sleep 很可能是不准确的。

Gevent 使用 libuv 或 libev 进行内部循环。我从这里得到了关于 libuv 如何处理轮询超时的以下描述

If the loop was run with the UV_RUN_NOWAIT flag, the timeout is 0.
If the loop is going to be stopped (uv_stop() was called), the timeout is 0.
If there are no active handles or requests, the timeout is 0.
If there are any idle handles active, the timeout is 0.
If there are any handles pending to be closed, the timeout is 0.
If none of the above cases matches, the timeout of the closest timer is taken, or if there are no active timers, infinity.

似乎当我们有 gevent sleep 时,实际上它会设置一个计时器,并且 libuv 循环使用最近的计时器的超时。

我真的怀疑这是根本原因:操作系统系统选择超时不准确!

我注意到 libuv 循环可以在 UV_RUN_NOWAIT 模式下运行,它会使循环超时为 0。如果没有 iOS 事件,那就没有睡眠。

它可能会导致一个 CPU 内核的负载达到 100%,但我可以接受。

于是我修改了gevent代码hub.py的函数run,如下

loop.run(nowait=True)

但是当我运行该工具时,我收到了“此操作将永远阻塞”的抱怨,如下所示

    gevent.sleep(0.001)
  File "C:\Python37\lib\site-packages\gevent\hub.py", line 159, in sleep
    hub.wait(t)
  File "src\gevent\_hub_primitives.py", line 46, in gevent.__hub_primitives.WaitOperationsGreenlet.wait
  File "src\gevent\_hub_primitives.py", line 55, in gevent.__hub_primitives.WaitOperationsGreenlet.wait
  File "src\gevent\_waiter.py", line 151, in gevent.__waiter.Waiter.get
  File "src\gevent\_greenlet_primitives.py", line 60, in gevent.__greenlet_primitives.SwitchOutGreenletWithLoop.switch
  File "src\gevent\_greenlet_primitives.py", line 60, in gevent.__greenlet_primitives.SwitchOutGreenletWithLoop.switch
  File "src\gevent\_greenlet_primitives.py", line 64, in gevent.__greenlet_primitives.SwitchOutGreenletWithLoop.switch
  File "src\gevent\__greenlet_primitives.pxd", line 35, in gevent.__greenlet_primitives._greenlet_switch
gevent.exceptions.LoopExit: This operation would block forever

所以我该怎么做?

标签: gevent

解决方案


是的,我终于找到了诀窍。

如果 libuv 循环运行模式不是 UV_RUN_DEFAULT,gevent 会做一些检查,如果 libuv 循环是 'nowait' 模式,它会说“这个操作将永远阻塞”。

这是有线的,实际上它不会永远阻塞。

无论如何,我只是将文件 libuv/loop.py 的第 473 行修改为如下


        if mode == libuv.UV_RUN_DEFAULT:
            while self._ptr and self._ptr.data:

                self._run_callbacks()
                self._prepare_ran_callbacks = False

                # here, change from UV_RUN_ONCE to UV_RUN_NOWAIT
                ran_status = libuv.uv_run(self._ptr, libuv.UV_RUN_NOWAIT)

之后运行加载工具,哇……果然和我预想的一样,TPS和我设置的很接近,但是一个核心负载是100%。

这完全可以接受,因为它是负载测试工具。

因此,如果我们有实时操作系统内核,我们就不会费心去做。


推荐阅读