阻塞调用阻止了一个线程 - 如何在阻塞调用上超时?

时间:2013-11-05 08:41:33

标签: python multithreading timeout blocking

我有一个python程序,它运行一个外部程序并启动一个超时线程。超时线程应该倒计时10分钟,如果操作外部程序的脚本在那段时间内没有完成,它应该终止外部程序。

我的线程似乎在第一眼就能正常工作,我的主脚本和线程同时运行,没有任何问题。但是如果外部程序中出现弹出窗口,它会停止我的脚本,这样即使倒计时线程也会停止计数,因此完全失败了。

我认为问题是该脚本在外部程序的API中调用阻止功能,该功能被弹出窗口阻止。我理解为什么它阻止我的主程序,但不明白为什么它阻止我的倒计时线程。因此,一个可能的解决方案可能是为倒计时运行一个单独的脚本,但我希望尽可能保持干净,并且为此启动脚本似乎非常混乱。

我到处寻找线索,但我找不到多少。这里有对 gevent库的引用: background function in Python ,但这似乎是一项基本任务,我不想为此包含外部库。

我还找到了一个使用Windows多媒体计时器here的解决方案,但我以前从未使用过这个,恐怕代码不会灵活。脚本仅限Windows,但它可以在XP上的所有Windows上运行。

对于Unix我发现signal.alarm似乎完全符合我的要求,但它不适用于Windows。对此有何选择?

有关如何以最简化的方式使用此功能的任何想法?

这是我正在创建的简化线程(在IDLE中运行以重现问题):

import threading
import time

class timeToKill():
    def __init__(self, minutesBeforeTimeout):
        self.stop = threading.Event()
        self.countdownFrom = minutesBeforeTimeout * 60

    def startCountdown(self):
        self.countdownThread= threading.Thread(target=self.countdown, args=(self.countdownFrom,))
        self.countdownThread.start()

    def stopCountdown(self):
        self.stop.set()
        self.countdownThread.join()

    def countdown(self,seconds):
        for second in range(seconds):
            if(self.stop.is_set()):
                break
            else:
                print (second)
                time.sleep(1)

timeout = timeToKill(1)
timeout.startCountdown()
raw_input("Blocking call, waiting for input:\n")

2 个答案:

答案 0 :(得分:1)

对于快速和脏的线程,我通常使用子进程命令。它非常强大且独立于操作系统。它没有提供与线程和队列模块一样精细的控制,但对于程序的外部调用通常做得很好。注意必须谨慎使用shell = True。

#this can be any command
p1 = subprocess.Popen(["python", "SUBSCRIPTS/TEST.py", "0"], shell=True)

#the thread p1 will run in the background - asynchronously.  If you want to kill it after some time, then you need 

#here do some other tasks/computations
time.sleep(10)

currentStatus = p1.poll()
if currentStatus is None: #then it is still running
  try:
    p1.kill() #maybe try os.kill(p1.pid,2) if p1.kill does not work
  except:
    #do something else if process is done running - maybe do nothing?
    pass

答案 1 :(得分:1)

阻止另一个Python线程的函数调用的一个可能的解释是CPython使用全局解释器锁(GIL)并且阻塞API调用不释放它(注意:CPython在阻塞I / O调用时释放GIL因此你的{ {1}}示例应该按原样运行。)

如果您无法进行错误的API调用以释放GIL,那么您可以使用流程而不是线程,例如multiprocessing.Process而不是raw_input()(API是相同的)。不同的流程不受GIL的限制。