1

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

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

我假设问题是脚本在 API 中为外部程序调用了一个阻塞函数,该函数被弹出窗口阻塞。我明白它为什么会阻塞我的主程序,但不明白它为什么会阻塞我的倒计时线程。因此,一种可能的解决方案可能是为倒计时运行一个单独的脚本,但我希望它尽可能保持干净,为此启动一个脚本似乎真的很混乱。

我到处寻找线索,但没有找到太多线索。这里有对gevent 库的引用: python 中的后台函数 ,但这似乎是一项基本任务,我不想为此包含外部库。

我还在这里找到了一个使用 Windows 多媒体计时器的解决方案,但我以前从未使用过此功能,并且担心代码对此不灵活。脚本仅适用于 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")
4

2 回答 2

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
于 2013-11-05T13:53:00.237 回答
1

对阻塞另一个 Python 线程的函数调用的一种可能解释是 CPython 使用全局解释器锁 (GIL) 并且阻塞 API 调用不会释放它(注意:CPython 在阻塞 I/O 调用时释放 GIL,因此您的raw_input()示例应该像是)。

如果您不能进行有缺陷的 API 调用来释放 GIL,那么您可以使用进程而不是线程,例如,multiprocessing.Process而不是threading.Thread(API 相同)。不同的进程不受 GIL 的限制。

于 2013-11-05T15:49:56.467 回答