跨线程中止HTTP请求
我正在把我的一个项目从C#移植到Python,但在处理多线程问题时遇到了麻烦。这个问题和一个长时间运行的HTTP请求有关,这个请求是正常的(它会在服务器上发生某个事件时返回响应)。以下是我的总结:
我在一个单独的线程中使用urllib2
发送请求。当请求返回或超时时,主线程会收到通知。这一切都很好。然而,有时我需要中止这个正在进行的请求,并切换到一个不同的URL。我考虑了四种解决方案:
- 中止正在进行的请求。C#有
WebRequest.Abort()
,我可以跨线程调用它来中止请求。但Python的urllib2.Request
似乎只是一个纯数据类,实例只存储请求信息;响应和请求对象没有关联。所以我无法做到这一点。 - 中断线程。C#有
Thread.Interrupt()
,如果线程处于等待状态,它会引发ThreadInterruptedException
,或者下次进入等待状态时会这样做。(在监视器和文件/套接字I/O上等待都属于等待状态。)Python似乎没有类似的东西;似乎没有办法唤醒一个在I/O上被阻塞的线程。 - 给请求设置一个较短的超时时间。在超时后,检查一个“已中止”的标志。如果它是假的,就重新启动请求。
- 类似于选项3,给状态对象添加一个“已中止”标志,这样当请求最终以某种方式结束时,线程就知道不再需要响应,可以直接关闭自己。
选项3和4似乎是Python支持的唯一方案,但选项3是个糟糕的解决方案,而选项4会保持一个我不需要的连接。我希望能做个好网民,当我不再需要这个连接时把它关闭。有没有办法真的中止这个正在进行的请求,无论如何?
4 个回答
如果你没有其他选择,可能可以看看这个“可终止线程”的代码片段,也许对你有帮助。不过我和Spike Gronim
的看法一样,还是建议你使用gevent
。
这段话和Spike Gronim的回答有点相似,但更加直接。
可以考虑用twisted来重写这个。你可能需要创建一个twisted.web.http.HTTPClient
的子类,特别是要实现handleResponsePart
方法来处理客户端的交互(如果你不需要在响应结束前查看内容,可以用handleResponseEnd
)。如果你想提前关闭连接,只需在客户端协议上调用loseConnection
方法就可以了。
可以考虑使用gevent。Gevent使用一种叫做绿色线程(greenlets)的执行单元,这种绿色线程可以在输入输出(IO)操作时“阻塞”,其实就是“等着,直到IO准备好”。你可以有一个请求的绿色线程负责处理网络连接,还有一个主绿色线程来决定什么时候要停止这个请求。当你想要停止并切换到另一个网址时,主绿色线程就会结束请求的绿色线程。请求的绿色线程会捕捉到这个结束的信号,关闭它的网络连接,然后重新开始。
补充说明:Gevent和线程不兼容,所以要小心。你要么全程使用gevent,要么全程使用线程。其实在Python中,线程的效果也不太好,因为有个叫做全局解释器锁(GIL)的东西。