Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何优雅地中断urllib2下载?_Python_Real Time_Urllib2 - Fatal编程技术网

Python 如何优雅地中断urllib2下载?

Python 如何优雅地中断urllib2下载?,python,real-time,urllib2,Python,Real Time,Urllib2,我正在使用urlib2的build\u opener()创建OpenerDirector。我正在使用OpenerDirector获取一个较慢的页面,因此它有一个较大的超时 到目前为止,一切顺利 然而,在另一个线程中,我被告知中止下载——假设用户已选择退出GUI中的程序 有没有一种方法可以发出urllib2下载应该退出的信号?我看不到任何内置机制可以实现这一点。我只是将OpenerDirectory移到它自己的线程进程中,这样就可以安全地杀死它 注意:在python中没有“杀死”线程的方法(感谢J

我正在使用
urlib2
build\u opener()
创建
OpenerDirector
。我正在使用
OpenerDirector
获取一个较慢的页面,因此它有一个较大的超时

到目前为止,一切顺利

然而,在另一个线程中,我被告知中止下载——假设用户已选择退出GUI中的程序


有没有一种方法可以发出urllib2下载应该退出的信号?

我看不到任何内置机制可以实现这一点。我只是将OpenerDirectory移到它自己的线程进程中,这样就可以安全地杀死它


注意:在python中没有“杀死”线程的方法(感谢JBernardo)。但是,可以在线程中插入,但如果线程在套接字上阻塞,则这可能不起作用。

下面是另一种方法的开始。它的工作原理是扩展httplib堆栈的一部分,以包含服务器响应的非阻塞检查。要在线程中实现这一点,您必须进行一些更改。还要注意,它使用了一些未记录的urllib2和httplib,因此最终的解决方案可能取决于您使用的Python版本(我有2.7.3)。在urllib2.py和httplib.py文件中浏览;它们可读性很强

import urllib2, httplib, select, time

class Response(httplib.HTTPResponse):
    def _read_status(self):
        ## Do non-blocking checks for server response until something arrives.
        while True:
            sel = select.select([self.fp.fileno()], [], [], 0)
            if len(sel[0]) > 0:
                break
            ## <--- Right here, check to see whether thread has requested to stop
            ##      Also check to see whether timeout has elapsed
            time.sleep(0.1)
        return httplib.HTTPResponse._read_status(self)

class Connection(httplib.HTTPConnection):
    response_class = Response

class Handler(urllib2.HTTPHandler):
    def http_open(self, req):
        return self.do_open(Connection, req)

h = Handler()
o = urllib2.build_opener(h)
f = o.open(url)
print f.read()
导入urllib2,httplib,选择,时间
类响应(httplib.HTTPResponse):
def_读取_状态(自身):
##对服务器响应执行非阻塞检查,直到有响应到达。
尽管如此:
sel=select.select([self.fp.fileno()]、[]、[]、0)
如果len(sel[0])>0:
打破

##没有明确的答案。有几个丑陋的。

起初,我在问题中加入了被拒绝的想法。很明显,没有正确的答案,我决定将各种次优的备选方案作为列表答案发布。其中一些是受到评论的启发,谢谢

图书馆支持 理想的解决方案是,
OpenerDirector
提供一个cancel操作符

事实并非如此。库编写者注意:如果您提供了长时间的慢操作,那么如果人们要在实际应用程序中使用它们,您需要提供一种取消它们的方法

减少超时 作为其他人的一般解决方案,这可能有效。超时时间越短,它对环境变化的响应就越快。但是,如果下载没有在超时时间内完全完成,也会导致下载失败,因此这是一种折衷。在我的情况下,这是站不住脚的

分块阅读下载内容。 同样,作为一般解决方案,这可能会奏效。如果下载包含非常大的文件,则可以在读取块后终止

不幸的是,如果(在我的例子中)延迟是在接收第一个字节,而不是文件的大小,这将没有帮助

杀死整个线程。 虽然有一些攻击性的技术可以杀死线程,但这取决于操作系统。特别是,它们可能导致死锁发生。见Eli Bendersky(通过@JBernardo)

只是没有反应 如果用户触发了中止操作,最简单的方法可能是不响应,在打开操作完成之前不对请求采取行动

用户是否可以接受这种无响应(提示:否!),取决于您的项目

它还会继续向服务器提出请求,即使已知结果是不需要的

让它在另一个线程中逐渐消失。 如果创建一个单独的线程来运行该操作,然后以可中断的方式与该线程通信,则可以放弃被阻止的线程,转而开始执行下一个操作。最终,线程将解锁,然后可以正常关闭

该线程应该是一个守护进程,因此它不会阻止应用程序的完全关闭

这将为用户提供响应能力,但这意味着服务器需要继续支持它,即使结果不需要

重写基于轮询的套接字方法。 如@Luke所述,可以为标准Python库提供(脆弱的、不可移植的)扩展

他的解决方案将套接字操作从阻塞更改为轮询。另一个可能允许通过
socket.shutdown()
方法关闭(如果这确实会中断阻塞的套接字-未测试)

基于Twisted的解决方案可能更干净。见下文

将套接字替换为异步的、非基于线程的库。 该框架为事件驱动的网络操作提供了一组替换库。我理解这意味着所有不同的通信都可以由一个线程处理,没有阻塞

破坏 可以导航
OpenerDirector
,找到阻塞的基级套接字,并直接破坏它(socket.shutdown()
是否足够?)使其返回

恶心

把它放在一个单独的(可杀死的)过程中 读取套接字的线程可以移动到单独的进程中,进程间通信可以用来传输结果。客户端可以提前中止此IPC,然后终止整个进程

要求Web服务器取消
如果您可以控制正在读取的web服务器,则可能会发送一条单独的消息,要求它关闭套接字。这应该会导致被阻止的客户端做出反应。

我发现一种方法,可以将所有与urllib相关的作业放在最合适的线程中,因为urllib具有阻塞性质。然后可以完全中止任务,包括请求。终止线程确实是不安全的,但异常引发应该是安全的

这就是如何在线程()中引发异常:

如果套接字此时处于阻塞(连接)状态,将立即引发异常
import ctypes
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(your_thread.ident),
                                           ctypes.py_object(your_exception))