跨线程中止HTTP请求

4 投票
4 回答
865 浏览
提问于 2025-04-16 22:45

我正在把我的一个项目从C#移植到Python,但在处理多线程问题时遇到了麻烦。这个问题和一个长时间运行的HTTP请求有关,这个请求是正常的(它会在服务器上发生某个事件时返回响应)。以下是我的总结:

我在一个单独的线程中使用urllib2发送请求。当请求返回或超时时,主线程会收到通知。这一切都很好。然而,有时我需要中止这个正在进行的请求,并切换到一个不同的URL。我考虑了四种解决方案:

  1. 中止正在进行的请求。C#有WebRequest.Abort(),我可以跨线程调用它来中止请求。但Python的urllib2.Request似乎只是一个纯数据类,实例只存储请求信息;响应和请求对象没有关联。所以我无法做到这一点。
  2. 中断线程。C#有Thread.Interrupt(),如果线程处于等待状态,它会引发ThreadInterruptedException,或者下次进入等待状态时会这样做。(在监视器和文件/套接字I/O上等待都属于等待状态。)Python似乎没有类似的东西;似乎没有办法唤醒一个在I/O上被阻塞的线程。
  3. 给请求设置一个较短的超时时间。在超时后,检查一个“已中止”的标志。如果它是假的,就重新启动请求。
  4. 类似于选项3,给状态对象添加一个“已中止”标志,这样当请求最终以某种方式结束时,线程就知道不再需要响应,可以直接关闭自己。

选项3和4似乎是Python支持的唯一方案,但选项3是个糟糕的解决方案,而选项4会保持一个我不需要的连接。我希望能做个好网民,当我不再需要这个连接时把它关闭。有没有办法真的中止这个正在进行的请求,无论如何?

4 个回答

0

如果你没有其他选择,可能可以看看这个“可终止线程”的代码片段,也许对你有帮助。不过我和Spike Gronim的看法一样,还是建议你使用gevent

1

这段话和Spike Gronim的回答有点相似,但更加直接。

可以考虑用twisted来重写这个。你可能需要创建一个twisted.web.http.HTTPClient的子类,特别是要实现handleResponsePart方法来处理客户端的交互(如果你不需要在响应结束前查看内容,可以用handleResponseEnd)。如果你想提前关闭连接,只需在客户端协议上调用loseConnection方法就可以了。

2

可以考虑使用gevent。Gevent使用一种叫做绿色线程(greenlets)的执行单元,这种绿色线程可以在输入输出(IO)操作时“阻塞”,其实就是“等着,直到IO准备好”。你可以有一个请求的绿色线程负责处理网络连接,还有一个主绿色线程来决定什么时候要停止这个请求。当你想要停止并切换到另一个网址时,主绿色线程就会结束请求的绿色线程。请求的绿色线程会捕捉到这个结束的信号,关闭它的网络连接,然后重新开始。

补充说明:Gevent和线程不兼容,所以要小心。你要么全程使用gevent,要么全程使用线程。其实在Python中,线程的效果也不太好,因为有个叫做全局解释器锁(GIL)的东西。

撰写回答