中止HTTP请求跨线程

时间:2011-08-02 18:45:16

标签: python multithreading

我正在从C#移植我的一个项目,并且无法解决Python中的多线程问题。该问题涉及预期的长期HTTP请求(当服务器上发生某个事件时,请求将响应)。以下是摘要:

我使用urllib2在单独的线程上发送请求。当请求返回或超时时,将通知主线程。这很好用。但是,在某些情况下,我需要中止此未完成的请求并切换到其他URL。我可以考虑四种解决方案:

  1. 中止未完成的请求。 C#有WebRequest.Abort(),我可以调用跨线程来中止请求。 Python urllib2.Request似乎是纯数据类,在该实例中只存储请求信息;响应未连接到Request对象。所以我不能这样做。
  2. 中断线程。 C#有Thread.Interrupt(),如果线程处于等待状态,或者下次进入这种状态,它将在线程中引发ThreadInterruptedException。 (等待监视器和文件/套接字I / O都处于等待状态。)Python似乎没有任何可比性;似乎没有办法唤醒在I / O上阻塞的线程。
  3. 在请求上设置低超时。超时时,检查“中止”标志。如果为false,请重新启动请求。
  4. 与选项3类似,向状态对象添加“aborted”标志,以便当请求最终以某种方式结束时,线程知道不再需要响应并且只关闭自己。
  5. 选项3和4似乎是Python支持的唯一选项,但选项3是一个可怕的解决方案,4将保持打开我不需要的连接。我希望成为一名优秀的网友并在我不再需要时关闭这种联系。有没有办法以这种或那种方式实际中止未完成的请求?

4 个答案:

答案 0 :(得分:2)

考虑使用gevent。 Gevent使用称为greenlets的非线程协作执行单元。 Greenlets可以“阻止”IO,这实际上意味着“在IO准备就绪之前进入睡眠状态”。您可以拥有一个拥有套接字的请求者greenlet和一个决定何时中止的主greenlet。当您想要中止并切换URL时,主greenlet会杀死请求者greenlet。请求者捕获结果异常,关闭其socket / urllib2请求,然后重新开始。

编辑添加:Gevent与线程不兼容,所以要小心。你必须要么一直使用gevent或者一直使用线程。由于GIL,python中的线程无论如何都是蹩脚的。

答案 1 :(得分:1)

类似Spike Gronim的回答,但更为沉重。

考虑用扭曲的方式重写它。您可能希望继承twisted.web.http.HTTPClient,特别是实现handleResponsePart来进行客户端交互(或handleResponseEnd如果您不需要在响应结束前查看它)。要尽早关闭连接,只需在客户端协议上调用loseConnection方法。

答案 2 :(得分:0)

如果您没有其他选择,那么“killable thread”的this snippet可能对您有用。但我与Spike Gronim的观点相同,建议使用gevent

答案 3 :(得分:0)

我使用谷歌发现了这个问题并使用了Spike Gronim的答案来提出:

from gevent import monkey
monkey.patch_all()
import gevent
import requests


def post(*args, **kwargs):
    if 'stop_event' in kwargs:
        stop_event = kwargs['stop_event']
        del kwargs['stop_event']
    else:
        stop_event = None

    req = gevent.spawn(requests.post, *args, **kwargs)

    while req.value is None:
        req.join(timeout=0.1)
        if stop_event and stop_event.is_set():
            req.kill()
            break

    return req.value

我认为它对其他人也有用。

它就像常规的request.post一样,但需要额外的关键字参数' stop_event'。这是一个线程。事件。如果设置了stop_event,请求将中止。

请谨慎使用,因为如果它没有等待连接或通信,它可以阻止GIL(如上所述)。它(gevent)似乎与这些天的线程兼容(通过猴子补丁)。