【问题标题】:Is it possible to kill the parent thread from within a child thread in python?是否可以从 python 的子线程中杀死父线程?
【发布时间】:2017-01-10 20:25:54
【问题描述】:

我在 Windows 上使用 Python 3.5.2。

我想运行一个 python 脚本,但保证不会超过 N 秒。如果 确实 花费了超过 N 秒,则应该引发异常,并且程序应该退出。最初我以为我可以在开始时启动一个线程,在抛出异常之前等待 N 秒,但这只会向计时器线程抛出异常,而不是向父线程抛出异常。例如:

import threading
import time

def may_take_a_long_time(name, wait_time):
    print("{} started...".format(name))
    time.sleep(wait_time)
    print("{} finished!.".format(name))

def kill():
    time.sleep(3)
    raise TimeoutError("No more time!")

kill_thread = threading.Thread(target=kill)
kill_thread.start()

may_take_a_long_time("A", 2)
may_take_a_long_time("B", 2)
may_take_a_long_time("C", 2)
may_take_a_long_time("D", 2)

这个输出:

A started...
A finished!.
B started...
Exception in thread Thread-1:
Traceback (most recent call last):
    File "C:\Program Files\Python35\lib\threading.py", line 914, in _bootstrap_inner
    self.run()
    File "C:\Program Files\Python35\lib\threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
    File "timeout.py", line 11, in kill
    raise TimeoutError("No more time!")
    TimeoutError: No more time!

B finished!.
C started...
C finished!.
D started...
D finished!.

这甚至是遥不可及的吗?我意识到我可以这样做:

import threading
import time

def may_take_a_long_time(name, wait_time, thread):
    if not thread.is_alive():
        return
    print("{} started...".format(name))
    time.sleep(wait_time)
    print("{} finished!.".format(name))

def kill():
    time.sleep(3)
    raise TimeoutError("No more time!")

kill_thread = threading.Thread(target=kill)
kill_thread.start()

may_take_a_long_time("A", 2, kill_thread)
may_take_a_long_time("B", 2, kill_thread)
may_take_a_long_time("C", 2, kill_thread)
may_take_a_long_time("D", 2, kill_thread)

但是,例如,如果调用了may_take_a_long_time("B", 60, kill_thread),则此方法会失败。

所以我想我的 TL;DR 问题是,对主线程本身设置时间限制的最佳方法是什么?

【问题讨论】:

  • 你根本不能杀死 Python 线程。只是没有API。而您在代码中所做的只是在一个新线程中引发一个异常,这不会影响主线程。
  • 时间限制的目的是什么?如果您不想等待某件事发生超过一定时间,您只需停止等待。
  • @ForceBru 我知道它不会影响主要的,那是我的问题。这是我最初的幼稚实现。我正在尝试找出如何确保一系列函数 (may_take_a_long_time) 花费的时间不超过 N 秒。

标签: python multithreading time


【解决方案1】:

你可以使用_thread.interrupt_main(这个模块在Python 2.7中被称为thread):

import time, threading, _thread

def long_running():
    while True:
        print('Hello')

def stopper(sec):
    time.sleep(sec)
    print('Exiting...')
    _thread.interrupt_main()

threading.Thread(target = stopper, args = (2, )).start()

long_running()

【讨论】:

  • 嘿,谢谢!这正是我一直在寻找的(而且速度也很快!)。你知道是否可以改变抛出的异常? KeyboardInterrupt 似乎与我正在做的事情不太相关。
  • @DJMcMayhem,文档说:这个函数只抛出KeyboardInterrupts。实际上,您可以在全局变量中涉及一些技巧,表明异常是由stopper 引发的,并以不同的方式处理这种情况。但是,如果您在主线程中捕获异常,长时间运行的函数将被终止,其结果将丢失,因此您可能希望在该函数本身内处理它
  • 我在子线程中使用了@ForceBru 的建议,并按照下面@rbanffy 的建议通过调用os._exit(0) 来处理KeyboardInterrupt
【解决方案2】:

如果父线程是根线程,可以试试os._exit(0)

import os
import threading
import time

def may_take_a_long_time(name, wait_time):
    print("{} started...".format(name))
    time.sleep(wait_time)
    print("{} finished!.".format(name))

def kill():
    time.sleep(3)
    os._exit(0)

kill_thread = threading.Thread(target=kill)
kill_thread.start()

may_take_a_long_time("A", 2)
may_take_a_long_time("B", 2)
may_take_a_long_time("C", 2)
may_take_a_long_time("D", 2)

【讨论】:

  • 不幸的是,这不起作用。我猜sys.exit() 仅限于调用的任何线程,没有任何方法可以改变父级的行为。
  • 这样效果更好,但我可能不太可能使用它,因为它实际上不会导致异常或抛出错误。
  • 您可以将其他内容返回给调用进程以发出超时信号。通常,返回 0 表示一切正常。我不确定你可以在 Windows 上返回什么(上次我检查过,在 DOS 中你可以返回一个 int,但那是很久以前的事了)。
【解决方案3】:

做到这一点的最干净的方法是有一个父线程定期检查的队列。如果孩子想要杀死父母,它会向队列发送一条消息(例如“DIE”)。父级检查队列,如果看到消息,它就会死掉。

    q = queue.Queue()
    bc = queue.Queue()          # backchannel

    def worker():
    while True:
            key = q.get()
            try:
                process_key(key)
            except ValueError as e:
                bc.put('DIE')
            q.task_done()

    # Start the threads
    for i in range(args.threads):
        threading.Thread(target=worker, daemon=True).start()

    for obj in object_farm():
        q.put(obj)
        try:
            back = bc.get(block=False)
        except queue.Empty:
            pass
        else:
            print("Data received on backchannel:",back)
            if back=='DIE':
                raise RuntimeError("time to die")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多