Подтвердить что ты не робот

Как остановить поток цикла в Python?

Какой правильный способ сказать циклному потоку прекратить цикл?

У меня довольно простая программа, которая пингует указанный узел в отдельном классе threading.Thread. В этом классе он спит 60 секунд, повторы запускаются до тех пор, пока приложение не завершится.

Я хотел бы использовать кнопку "Стоп" в wx.Frame, чтобы попросить поток цикла остановить. Он не должен сразу заканчивать нить, он может просто прекратить цикл, когда он просыпается.

Вот мой класс threading (обратите внимание: я еще не реализовал цикл, но он, вероятно, попадет под метод run в PingAssets)

class PingAssets(threading.Thread):
    def __init__(self, threadNum, asset, window):
        threading.Thread.__init__(self)
        self.threadNum = threadNum
        self.window = window
        self.asset = asset

    def run(self):
        config = controller.getConfig()
        fmt = config['timefmt']
        start_time = datetime.now().strftime(fmt)
        try:
            if onlinecheck.check_status(self.asset):
                status = "online"
            else:
                status = "offline"
        except socket.gaierror:
            status = "an invalid asset tag."
        msg =("{}: {} is {}.   \n".format(start_time, self.asset, status))
        wx.CallAfter(self.window.Logger, msg)

И в моем фрейме wxPyhton я вызываю эту функцию из кнопки "Пуск":

def CheckAsset(self, asset):
        self.count += 1
        thread = PingAssets(self.count, asset, self)
        self.threads.append(thread)
        thread.start()
4b9b3361

Ответ 1

Это было задано ранее в Stack. См. Следующие ссылки:

В основном вам просто нужно настроить поток с помощью функции остановки, которая устанавливает значение контрольной суммы, которое будет проверять поток. В вашем случае у вас будет что-то в вашем цикле, чтобы проверить значение дозорного, чтобы увидеть, изменилось ли оно, и если оно есть, цикл может сломаться, и поток может умереть.

Ответ 2

Резьбовая функция остановки

Вместо подкласса threading.Thread можно изменить функцию, позволяющую остановка с помощью флага.

Нам нужен объект, доступный для запущенной функции, на который мы установили, что флаг перестает работать.

Мы можем использовать объект threading.currentThread().

import threading
import time


def doit(arg):
    t = threading.currentThread()
    while getattr(t, "do_run", True):
        print ("working on %s" % arg)
        time.sleep(1)
    print("Stopping as you wish.")


def main():
    t = threading.Thread(target=doit, args=("task",))
    t.start()
    time.sleep(5)
    t.do_run = False
    t.join()

if __name__ == "__main__":
    main()

Фокус в том, что работающий поток может иметь дополнительные свойства. Решение строится по предположениям:

  • поток имеет свойство "do_run" со значением по умолчанию True
  • управляющий родительский процесс может назначить начальному потоку свойство "do_run" на False.

Запустив код, получим следующий вывод:

$ python stopthread.py                                                        
working on task
working on task
working on task
working on task
working on task
Stopping as you wish.

Pill to kill - используя событие

Другой альтернативой является использование threading.Event в качестве аргумента функции. Это по умолчанию False, но внешний процесс может "установить его" (до True), и функция может узнайте об этом с помощью функции wait(timeout).

Мы можем wait с нулевым таймаутом, но мы также можем использовать его в качестве таймера спящего режима (используется ниже).

def doit(stop_event, arg):
    while not stop_event.wait(1):
        print ("working on %s" % arg)
    print("Stopping as you wish.")


def main():
    pill2kill = threading.Event()
    t = threading.Thread(target=doit, args=(pill2kill, "task"))
    t.start()
    time.sleep(5)
    pill2kill.set()
    t.join()

Остановка нескольких потоков с помощью одной таблетки

Лучше увидеть преимущество таблетки, чтобы убить, если нам нужно остановить несколько потоков сразу, поскольку одна таблетка будет работать для всех.

doit не изменится вообще, только main обрабатывает потоки немного по-другому.

def main():
    pill2kill = threading.Event()
    tasks = ["task ONE", "task TWO", "task THREE"]

    def thread_gen(pill2kill, tasks):
        for task in tasks:
            t = threading.Thread(target=doit, args=(pill2kill, task))
            yield t

    threads = list(thread_gen(pill2kill, tasks))
    map(threading.Thread.start, threads)
    time.sleep(5)
    pill2kill.set()
    map(threading.Thread.join, threads)

Ответ 3

Я прочитал другие вопросы о Stack, но я все еще немного смутился в общении между классами. Вот как я подошел к нему:

Я использую список для хранения всех моих потоков в методе __init__ моего класса wxFrame: self.threads = []

Как рекомендуется в Как остановить поток цикла в Python? Я использую сигнал в моем классе потоков, который установлен в True при инициализации класса threading.

class PingAssets(threading.Thread):
    def __init__(self, threadNum, asset, window):
        threading.Thread.__init__(self)
        self.threadNum = threadNum
        self.window = window
        self.asset = asset
        self.signal = True

    def run(self):
        while self.signal:
             do_stuff()
             sleep()

и я могу остановить эти потоки, перебирая мои потоки:

def OnStop(self, e):
        for t in self.threads:
            t.signal = False