终止长时间运行的Python线程
在Python中,如何优雅地结束那些运行时间过长的线程呢?我不能使用SIGALRM,因为
如果在同一个程序中同时使用信号和线程,需要特别小心。使用信号和线程的关键点是:所有的信号操作都必须在主线程中进行。任何线程都可以执行alarm()、getsignal()、pause()、setitimer()或getitimer()这些操作;但是只有主线程可以设置新的信号处理器,并且只有主线程会接收到信号(这一点是Python信号模块强制执行的,即使底层线程实现支持向单独线程发送信号)。这意味着信号不能用作线程之间的通信方式,应该使用锁来代替。
更新:在我的情况下,每个线程都在阻塞——它正在使用urllib2模块下载网页,有时候在极慢的网站上操作会花费太多时间。这就是我想要结束这些慢线程的原因。
4 个回答
使用同步对象,并请求线程终止。简单来说,就是要写一些合作的处理方式。
如果你直接强行结束Python解释器下的线程,可能会发生各种奇怪的事情,这不仅仅是在Python中,大多数运行环境都有这个问题。
举个例子,如果你在一个线程打开了一个文件后就把它杀掉,那么这个文件就不会被关闭,直到应用程序完全结束。
正如Alex Martelli所建议的,你可以使用多进程模块。这和线程模块很相似,所以你可以很容易地开始。你的代码可以像这样:
import multiprocessing
def get_page(*args, **kwargs):
# your web page downloading code goes here
def start_get_page(timeout, *args, **kwargs):
p = multiprocessing.Process(target=get_page, args=args, kwargs=kwargs)
p.start()
p.join(timeout)
if p.is_alive():
# stop the downloading 'thread'
p.terminate()
# and then do any post-error processing here
if __name__ == "__main__":
start_get_page(timeout, *args, **kwargs)
当然,你需要以某种方式获取你下载页面的返回值。为此,你可以使用multiprocessing.Pipe或者multiprocessing.Queue(或者多进程模块提供的其他方法)。你可以在这里找到更多信息和示例。
最后,多进程模块在Python 2.6中已经包含。如果你使用的是Python 2.5或2.4,也可以在pypi上找到(你可以使用easy_install multiprocessing
)或者直接访问pypi手动下载和安装这些包。
注意:我知道这个问题已经发布了一段时间。我遇到了类似的问题,偶然间看到了这里,看到Alex Martelli的建议。于是我把它应用到我的问题上,并决定分享出来。(我想感谢Alex给我指明了方向。)
因为直接强制结束一个正在执行阻塞操作的线程是不可行的,所以更好的方法是,如果可以的话,尽量避免使用线程,而是选择其他不会出现这种问题的多任务处理方式。
对于提问者的具体情况(线程的工作是下载网页,有些线程因为网站的问题而一直卡住),理想的解决方案是twisted,因为它通常适合处理网络任务。在其他情况下,使用multiprocessing可能会更好。
更一般来说,当线程出现无法解决的问题时,我建议换用其他多任务处理方式,而不是尝试一些极端的方法来让线程完成那些在CPython中不适合的任务。