Нет никакого способа напрямую взаимодействовать с другим потоком, кроме основного потока. В то время как некоторые платформы предлагают отмену потока или уничтожение семантики, 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 потоков.