关于多线程:当没有数据时,是否可以阻止python的http.client.HTTPResponse.read()挂起?

Is it possible to prevent python's http.client.HTTPResponse.read() from hanging when there is no data?

我正在使用Python http.client.HTTPResponse.read()从流中读取数据。也就是说,服务器会永久保持连接打开,并在数据可用时定期发送数据。没有预期的响应时间。特别是,我正在通过Twitter Streaming API获取推文。

为此,我重复调用http.client.HTTPResponse.read(1)来获取响应,一次一个字节。问题是如果没有要读取的数据,程序将挂在该行上,这段时间不存在(当没有推文进入时)。

我正在寻找一种方法来获取HTTP响应的单个字节(如果可用),但如果没有数据要读取,它将立即失败。

我已经读过你可以在创建连接时设置超时,但是在连接上设置超时会使其长时间保持打开等待数据进入的整个目的。我不想设置超时,如果有数据需要读取,我想读取数据,如果没有数据则想要读取,如果没有等待,我想读取数据。

我想用现在的东西(使用http.client)来做这件事,但是如果我必须使用不同的库来做这件事,那就这样吧。我试图完全自己写这个,所以建议我使用别人已经编写的Python的Twitter API并不是我想要的。

此代码获取响应,它在与主要代码的单独线程中运行:

1
2
3
4
5
6
7
8
9
10
while True:
    try:
        readByte = dc.request.read(1)
    except:
        readByte = []

    if len(byte) != 0:
        dc.responseLock.acquire()
        dc.response = dc.response + chr(byte[0])
        dc.responseLock.release()

请注意,请求存储在dc.request中,响应存储在dc.response中,这些是在别处创建的。 dc.responseLockLock,可防止多个线程一次访问dc.response

通过在单独的线程上运行,主线程可以获得dc.response,其中包含到目前为止收到的整个响应。新数据在没有阻塞主线程的情况下添加到dc.response

这在运行时非常有效,但是当我希望它停止时我会遇到问题。我将while语句更改为while not dc.twitterAbort,因此当我想中止此线程时,我只需将dc.twitterAbort设置为True,线程就会停止。

但事实并非如此。此线程在此后保持很长时间,卡在dc.request.read(1)部分上。必须有某种超时,因为它最终会返回到while语句并停止该线程,但这需要大约10秒才能发生。

如果它停留在read()的调用上,如何让我的线程立即停止?

再次,这种方法正在努力获得推文,问题只在于让它停止。如果我以完全错误的方式解决这个问题,请随意指出我正确的方向。我是Python的新手,所以我可能会忽略一些更简单的方法。


您的想法并不新鲜,有一些操作系统机制(*)可确保应用程序在保证不阻塞时仅调用与I / O相关的系统调用。这些机制通常由异步I / O框架使用,例如龙卷风或gevent。使用其中之一,您会发现在应用程序等待I / O事件时"运行"代码非常容易,例如等待套接字上的传入数据。

如果您使用gevent的monkey-patching方法,则可以按要求继续使用http.client。您只需要习惯gevent / greenlets引入的协作调度范例,其中您的执行流程在子例程之间"跳转"。

当然,您也可以在另一个线程中执行阻塞I / O(就??像您一样),这样它就不会影响主线程的响应能力。关于你的"如何让我的线程立即停止"问题:

  • 强制在系统调用中阻塞的线程停止通常不是一个干净甚至有效的进程(也看到有没有办法在Python中杀死一个线程?)。要么 - 如果你的应用程序已经完成了它的工作 - 你取消了整个过程,这也影响了所有包含的线程,或者你只是留下线程并给它足够的时间根据需要终止(你引用的这10秒)不是问题 - 是吗?)

  • 如果您不希望在应用程序的任何位置进行长时间阻塞系统调用(无论是否在主线程中),请使用上述技术来阻止系统调用。

(*)见例如http://man7.org/linux/man-pages/man2/open.2.html中的O_NONBLOCK选项