Как закрыть все темы с бесконечными циклами? (с _thread! больше ничего!) - PullRequest
0 голосов
/ 26 июня 2018
import _thread
import time

def test1():
    while True:
        time.sleep(1)
        print('TEST1')

def test2():
    while True:
        time.sleep(3)
        print('TEST2')

try:
    _thread.start_new_thread(test1,())
    _thread.start_new_thread(test2,())
except:
    print("ERROR")

Как я могу остановить два потока, например, в случае KeyboardInterrupts? Потому что для «кроме KeyboardInterrupt» потоки все еще работают: /

Важно: Вопрос о закрытии темы только с модулем _thread! Возможно ли это?

1 Ответ

0 голосов
/ 26 июня 2018

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

Итак, обычное решение - использовать какой-то сигнал, чтобы сказать всем выйти. Один из вариантов - флаг done с Lock вокруг него:

done = False
donelock = _thread.allocate_lock()

def test1():
    while True:
        try:
            donelock.acquire()
            if done:
                return
        finally:
            donelock.release()
        time.sleep(1)
        print('TEST1')

_thread.start_new_thread(test1,())
time.sleep(3)
try:
    donelock.acquire()
    done = True
finally:
    donelock.release()

Конечно, то же самое будет намного чище, если вы используете threading (или другой высокоуровневый API, такой как потоки Qt). Кроме того, вы можете использовать Condition или Event, чтобы фоновые потоки выходили как можно скорее, а не только после того, как их следующая sleep завершится.

done = threading.Event()

def test1():
    while True:
        if done.wait(1):
            return
        print('TEST1')

t1 = threading.Thread(target=test1)
t1.start()
time.sleep(3)
done.set()

Модуль _thread, конечно, не имеет Event или Condition, но вы всегда можете создать его самостоятельно - или просто , заимствуя из threading источника .


Или, если вы хотите, чтобы потоки уничтожались асинхронно (что, очевидно, небезопасно, если они, например, пишут файлы, но если они просто выполняют вычисления или загружают файлы и т. П., То вам все равно о, если вы отменяете, это нормально), threading делает это еще проще:

t1 = threading.Thread(target=test1, daemon=True)

В качестве примечания: поведение, которое вы видите, на разных платформах ненадежно:

  • Фоновые потоки, созданные с помощью _thread, могут продолжать работать, либо частично очищаться, либо прерываться. Поэтому, когда вы используете _thread в переносимом приложении, вы должны написать код, который может обрабатывать любое из трех.
  • KeyboardInterrupt может быть доставлено в произвольный поток, а не в основной поток. Если это так, он обычно убивает этот поток, если вы не настроили обработчик. Так что, если вы используете _thread, вы обычно хотите обработать KeyboardInterrupt и позвонить _thread.interrupt_main().

Кроме того, я не думаю, что ваш except: делает то, что вы думаете. Это try покрывает только start_new_thread звонки. Если потоки запускаются успешно, основной поток выходит из блока try и достигает конца программы. Если возникает KeyboardInterrupt или другое исключение, except: не будет срабатывать. (Кроме того, если вы хотите иметь возможность понять, что делает ваш код, использовать except: и даже не регистрировать, какое исключение было обработано. Это действительно плохая идея.) Предположительно, на вашей платформе фоновые потоки продолжают работать, и на них блокируются основные потоки (и, вероятно, на уровне ОС, а не на уровне Python, поэтому вы не можете написать код, который бы там участвовал).

Если вы хотите, чтобы ваш основной поток продолжал работать, чтобы убедиться, что он может обрабатывать KeyboardInterrupt и что-то с ним (но смотри предостережения выше!), Вы должны дать ему код для продолжения работы:

try:
    while True:
        time.sleep(1<<31)
except KeyboardInterrupt:
    # background-thread-killing code goes here.    

1. TerminateThread в Windows делает невозможным выполнение всей очистки, необходимой Python. pthread_cancel в системах POSIX, таких как Linux и macOS, делает это возможным, но очень сложно. И семантика достаточно различна между двумя, что попытка написать кроссплатформенную оболочку была бы кошмаром. Не говоря уже о том, что Python поддерживает системы (в основном более старые Unixes), которые не имеют полного API pthread или даже имеют совершенно другой API потоков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...