我正在使用 Python http.client.HTTPResponse.read()
从流中读取数据。也就是说,服务器保持连接永远打开,并在数据可用时定期发送数据。没有预期的响应长度。特别是,我正在通过 Twitter Streaming API 获取推文.
为此,我反复调用 http.client.HTTPResponse.read(1)
来获取响应,一次一个字节。问题是,如果没有数据可供读取,程序将卡在该行上,而这种情况在很长一段时间内都没有(当没有推文传入时)。
我正在寻找一种方法,该方法将获取 HTTP 响应的单个字节(如果可用),但如果没有数据可读取,该方法将立即失败。
我已经 read that you can set a timeout when the connection is created ,但是在连接上设置超时破坏了让它长时间打开等待数据进来的全部目的。我不想设置超时,如果有数据要读取我想读取数据,如果没有,则失败,根本不等待。
我想用我现在拥有的东西(使用 http.client
)来做这件事,但如果绝对有必要,我会使用不同的库来做这件事,那就这样吧。我正在尝试完全自己编写,因此建议我使用其他人已经为 Python 编写的 Twitter API 并不是我想要的。
此代码获取响应,它在与主线程不同的单独线程中运行:
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.responseLock
是一个 Lock
,可防止 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 框架使用,例如 tornado 或 gevent。使用其中之一,您会发现在应用程序等待 I/O 事件(例如等待套接字上的传入数据)的“同时”运行代码非常容易。
如果您使用 gevent 的猴子修补方法,您可以根据要求继续使用 http.client
。您只需要习惯 gevent/greenlets 引入的协作调度范式,您的执行流程在子例程之间“跳跃”。
当然,您也可以在另一个线程中执行阻塞 I/O(就像您所做的那样),这样就不会影响主线程的响应能力。关于您的“如何让我的线程立即停止”问题:
强制停止在系统调用中阻塞的线程通常不是一个干净甚至有效的进程(另请参阅 Is there any way to kill a Thread in Python? )。要么——如果你的应用程序已经完成了它的工作——你取消了整个进程,这也会影响所有包含的线程,或者你只是让线程保持原状并给它尽可能多的时间来终止所需的时间(你指的是这 10 秒不是问题——是吗?)
如果您不希望在您的应用程序中的任何地方(无论是否在主线程中)进行此类长时间阻塞的系统调用,请使用上述技术来防止阻塞系统调用。
(*) 参见例如http://man7.org/linux/man-pages/man2/open.2.html 中的 O_NONBLOCK 选项
关于python - 是否可以防止python的http.client.HTTPResponse.read()在没有数据时挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28400885/