【问题标题】:gevent: I/O completes before timeout, but timeout still occursgevent:I/O 在 timeout 之前完成,但仍会发生 timeout
【发布时间】:2017-05-04 14:08:24
【问题描述】:

我有两个绿叶。第一个 greenlet 在触发 HTTP 请求之前启动 7 秒超时,我知道该请求需要 5 秒才能完成。第二个 greenlet 正在执行一些 CPU 密集型任务(由 time.sleep 模拟),需要 10 秒才能完成。代码:

from gevent import monkey; monkey.patch_socket()

import time

import gevent
import requests


def f1():
    try:
        with gevent.Timeout(7):
            print "f1: downloading our page in 5 seconds"
            requests.get('http://httpbin.org/delay/5')
    except gevent.Timeout:
        print "Timed out."
    else:
        print "Finished!"


def f2():
    print "f2: being greedy for 10 seconds"
    time.sleep(10)


if __name__ == '__main__':
    g1 = gevent.spawn(f1)
    g2 = gevent.spawn(f2)
    gevent.joinall([g1, g2])

当我运行此代码时,即使我知道 HTTP 请求已完成,也会触发超时:

$ python test.py
f1: downloading our page in 5 seconds
f2: being greedy for 10 seconds
Timed out.

time.sleep 调用显然是造成超时的原因,但我实际上并不关心这需要多长时间。我真正想要限制的是 HTTP 请求的持续时间。

有没有办法编写此代码,以便仅在 HTTP 请求未完成时才发生超时?有没有办法在超时事件之前先处理 I/O 事件?

【问题讨论】:

    标签: python timeout gevent


    【解决方案1】:

    NB - 阻塞调用 - 像 CPU 绑定函数 - 不应分配给 greenlet,因为根据定义,它们会阻止所有其他 greenlet 运行。

    结合使用原生线程池,如 gevent.threadpool.ThreadPool 提供的用于阻塞任务和 greenlet 用于非阻塞任务的线程池。

    示例代码中的行为是因为代码没有修补时间,time.sleep(10) 导致 gevent 超时,因为它阻塞了整个事件循环,这意味着所有 greenlets。

    这按预期工作:

    from gevent import monkey
    monkey.patch_all()
    
    import time
    
    import gevent
    import requests
    
    
    def f1():
        try:
            with gevent.Timeout(7):
                print "f1: downloading our page in 5 seconds"
                requests.get('http://httpbin.org/delay/5')
        except gevent.Timeout:
            print "Timed out."
        else:
            print "Finished!"
    
    
    def f2():
        print "f2: being greedy for 10 seconds"
        time.sleep(10)
    
    
    if __name__ == '__main__':
        g1 = gevent.spawn(f1)
        g2 = gevent.spawn(f2)
        gevent.joinall([g1, g2])
    

    输出:

    f1: downloading our page in 5 seconds
    f2: being greedy for 10 seconds
    Finished!
    

    也可以使用gevent.sleep来避免打补丁。

    【讨论】:

    • 我正在使用time.sleep,因为我正在尝试模拟一个 CPU 密集型任务,这确实会阻塞整个事件循环。但是你让我想到,也许我一开始就不应该在事件循环中做这些事情——可能是gevent.threadpool 的意思。
    • 相当。阻塞任务不属于greenlets,因为它们会阻塞整个事件循环。将线程池用于阻塞或 CPU 绑定函数,将 greenlet 用于非阻塞调用。
    猜你喜欢
    • 2010-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-21
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多