我正在从C#移植我的一个项目,并且无法解决Python中的多线程问题。该问题涉及预期的长期HTTP请求(当服务器上发生某个事件时,请求将响应)。以下是摘要:
我使用urllib2
在单独的线程上发送请求。当请求返回或超时时,将通知主线程。这很好用。但是,在某些情况下,我需要中止此未完成的请求并切换到其他URL。我可以考虑四种解决方案:
WebRequest.Abort()
,我可以调用跨线程来中止请求。 Python urllib2.Request
似乎是纯数据类,在该实例中只存储请求信息;响应未连接到Request对象。所以我不能这样做。Thread.Interrupt()
,如果线程处于等待状态,或者下次进入这种状态,它将在线程中引发ThreadInterruptedException
。 (等待监视器和文件/套接字I / O都处于等待状态。)Python似乎没有任何可比性;似乎没有办法唤醒在I / O上阻塞的线程。选项3和4似乎是Python支持的唯一选项,但选项3是一个可怕的解决方案,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)似乎与这些天的线程兼容(通过猴子补丁)。