Python 3 – Как мгновенно завершить поток?

В моем коде (сложное приложение с графическим интерфейсом с Tkinter) у меня есть поток, определенный в пользовательском объекте (индикатор выполнения). Он запускает функцию с циклом while следующим образом:

def Start(self):
    while self.is_active==True:
        do it..
        time.sleep(1)
        do it..
        time.sleep(1)
    
def Stop(self):
    self.is_active=False

Он может завершиться только тогда, когда другой фрагмент кода, размещенный в другом потоке, изменит атрибут self.is_active с помощью метода self.Stop(). У меня такая же ситуация в другом пользовательском объекте (счетчик), и они оба должны работать вместе, когда работает другой поток (основной).

Код работает, но я понял, что два потока, связанные с индикатором выполнения и счетчиком, не завершаются мгновенно, как я хотел, потому что, прежде чем завершаться, им нужно дождаться окончания своих функций, а эти медленные из-за инструкции time.sleep(1). С точки зрения пользователя это означает видеть конец основного потока с индикатором выполнения и счетчиком, который завершается ПОЗЖЕ, и мне это не нравится.

Честно говоря, я не знаю, как решить эту проблему. Есть ли способ заставить поток немедленно завершиться, не дожидаясь окончания функции?

Мутабельность и переработка объектов в Python
Мутабельность и переработка объектов в Python
Объекты являются основной конструкцией любого языка ООП, и каждый язык определяет свой собственный синтаксис для их создания, обновления и...
Другой маршрут в Flask Python
Другой маршрут в Flask Python
Flask - это фреймворк, который поддерживает веб-приложения. В этой статье я покажу, как мы можем использовать @app .route в flask, чтобы иметь другую...
14 Задание: Типы данных и структуры данных Python для DevOps
14 Задание: Типы данных и структуры данных Python для DevOps
Проверить тип данных используемой переменной, мы можем просто написать: your_variable=100
Python PyPDF2 - запись метаданных PDF
Python PyPDF2 - запись метаданных PDF
Python скрипт, который будет записывать метаданные в PDF файл, для этого мы будем использовать PDF ридер из библиотеки PyPDF2 . PyPDF2 - это...
Переменные, типы данных и операторы в Python
Переменные, типы данных и операторы в Python
В Python переменные используются как место для хранения значений. Пример переменной формы:
Почему Python - идеальный выбор для проекта AI и ML
Почему Python - идеальный выбор для проекта AI и ML
Блог, которым поделился Harikrishna Kundariya в нашем сообществе Developer Nation Community.
0
0
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Самый безопасный способ сделать это, не рискуя ошибкой сегментации, — вернуться.

def Start(self):
    while self.is_active==True:
        do it..
        if not self.is_active: return
        time.sleep(1)
        if not self.is_active: return
        do it..
        if not self.is_active: return
        time.sleep(1)
    
def Stop(self):
    self.is_active=False

Потоки python должны освобождать связанные ресурсы, и хотя «убить» поток можно с помощью некоторых трюков C, вы рискуете ошибкой сегментации или утечкой памяти.

Вот более чистый способ сделать это.

class MyError(Exception):
    pass
def Start(self):
    try:
        while self.is_active==True:
            do it..
            self.check_termination()
            time.sleep(1)
            self.check_termination()
            do it..
            self.check_termination()
            time.sleep(1)
    except MyError:
        return

def check_termination(self):
    if not self.is_active:
        raise MyError

И вы можете вызвать self.check_termination() из любой функции, чтобы завершить этот цикл, не обязательно изнутри Start напрямую.

Обновлено: решение ShadowRanger лучше обрабатывает «прерываемое ожидание», я просто сохраняю его для реализации переключателя уничтожения для потока, который можно проверить из любого места внутри потока.

Насколько чище второй бит кода? Похоже, что замена return на break в вашем исходном коде дает все те же преимущества (возможность запуска дополнительного кода после завершения цикла перед возвратом), и я не вижу никакой реальной пользы от определения пользовательского исключения для выхода из одного петля слоя, когда простая break достигает тех же результатов с меньшим количеством шаблонов.

ShadowRanger 21.11.2022 16:51

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

Ahmed AEK 21.11.2022 16:55

Эх. Я на самом деле согласен с тем, что в этом случае он не сжимается; Я не хочу скрывать, что там код может выйти из цикла. Это также не очень заметно; вы скрыли то, что на самом деле происходит, в отдельной функции; имя функции дает подсказку о том, что она делает, но две строки будут немного более заметными, чем однострочный код, спрятанный в остальной части кода.

ShadowRanger 21.11.2022 16:58
Ответ принят как подходящий

Во-первых, чтобы было ясно, жесткое уничтожение потока — ужасная идея для любого языка, и Python не поддерживает это; по крайней мере, риск того, что этот поток удерживает блокировку, которая никогда не будет разблокирована, что приведет к взаимоблокировке любого потока, который попытается получить ее, является фатальным недостатком.

Если вам вообще не нужен поток, вы можете создать его с аргументом daemon=True, и он умрет, если все потоки, не являющиеся демонами, в процессе завершились. Но если поток действительно должен умереть при правильной очистке (например, в нем могут быть операторы with или тому подобное, которые управляют очисткой ресурсов вне процесса, которые не будут очищены при завершении процесса), это не настоящее решение.

Тем не менее, вы можете избежать ожидания секунды или более, переключившись с использования простых bool и time.sleep на использование Event и использование метода .wait на нем. Это позволит немедленно прервать «сны» за небольшую плату, требующую от вас отменить свое состояние (поскольку Event.wait блокируется только тогда, когда оно ложно / не установлено, поэтому вам нужно, чтобы флаг основывался на том, когда вы должны остановиться, а не когда вы сейчас активны):

class Spam:
    def __init__(self):
        self.should_stop = threading.Event()  # Create an unset event on init
    
    def Start(self):
        while not self.should_stop.is_set():
            # do it..

            if self.should_stop.wait(1):
                break

            # do it..

            if self.should_stop.wait(1):
                break

    def Stop(self):
        self.should_stop.set()

В современном Python (3.1 и выше) метод wait возвращает True, если событие было установлено (в начале ожидания или потому, что оно было установлено во время ожидания), и False в противном случае, поэтому всякий раз, когда wait возвращает True, это означает, что вам сказали остановиться и можно сразу break не в курсе. Вы также получаете уведомление почти сразу, вместо того, чтобы ждать до одной секунды, прежде чем вы сможете проверить флаг.

Это не приведет к немедленному выходу настоящего кода «сделай это..», но из того, что вы сказали, похоже, что эта часть кода не такая уж длинная, поэтому ожидание ее завершения не так уж и много. хлопот.

Если вы действительно хотите сохранить атрибут is_active для проверки того, активен ли он, вы можете определить его как свойство, которое меняет значение Event на противоположное, например:

 @property
 def is_active(self):
     return not self.should_stop.is_set()

Другие вопросы по теме