Ответ 1
SIGQUIT (Ctrl + \) убьет все процессы даже в Python 2.x.
Вы также можете обновить до Python 3.x, где это поведение (только дочерний сигнал получает сигнал), кажется, исправлено.
Я запускаю программу Python, которая использует модуль многопроцессорности для создания некоторых рабочих потоков. Используя Pool.map
, они перескажут список файлов.
В какой-то момент я хотел бы остановить все и умереть script.
Обычно Ctrl+C
из командной строки выполняет это. Но, в данном случае, я думаю, что просто прерывает одного из рабочих и создается новый рабочий.
Итак, я заканчиваю запуск ps aux | grep -i python
и используя kill -9
для рассматриваемых идентификаторов процесса.
Есть ли лучший способ, чтобы сигнал прерывания приводил все к остановке?
SIGQUIT (Ctrl + \) убьет все процессы даже в Python 2.x.
Вы также можете обновить до Python 3.x, где это поведение (только дочерний сигнал получает сигнал), кажется, исправлено.
К сожалению, в Python 2.x действительно нет хорошего решения этой проблемы. Лучшим обходным решением, которое я знаю, является использование pool.map_async(...).get(timeout=<large number>)
вместо pool.map
. Проблема в том, что pool.map
делает вызов threading.Condition.wait()
, который по какой-то причине не может быть прерван Ctrl + C в Python 2.x(он работает в Python 3). Когда вы используете map_async()
, он вызывает threading.Condition.wait(timeout=<large number>)
, который заканчивает выполнение цикла ожидания занятости, который может быть прерван Ctrl + C.
Попробуйте сами:
c = threading.Condition()
try:
c.acquire()
c.wait() # You won't be able to interrupt this
except KeyboardInterrupt:
print("Caught it")
c = threading.Condition()
try:
c.acquire()
c.wait(timeout=100) # You CAN interrupt this
except KeyboardInterrupt:
print("Caught it")
Итак, чтобы ваш прерыватель map
прерывался, сделайте следующее:
if __name__ == "__main__":
p = multiprocessing.Pool()
try:
p.map_async(func, iterable).get(timeout=10000000)
except KeyboardInterrupt:
print("Caught it")
# Optionally try to gracefully shut down the worker processes here.
p.close()
# DON'T join the pool. You'll end up hanging.
Также обратите внимание, как указано в phihag, эта проблема исправлена в Python 3.4 (и, возможно, ранее в 3.x).
Есть несколько способов. Первый способ - отметить поток как демона, используя
в Threading,
myThread.setDaemon(true)
при многопроцессорной обработке,
myThread.daemon = True
Все потоки, помеченные как демон, заканчиваются основным потоком. Это не правильный способ сделать это, поскольку он не позволяет очищать потоки.
Следующий способ - прослушать KeyboardInterrupt
с помощью try-catch, а затем .join() такие потоки.
try:
myThread = MyThread()
except KeyboardInterrupt:
myThread.join()
Если ваш поток находится в цикле, вы можете использовать такое условие, как логическое, установить это значение в false, а когда условие ложно, оно выполняет очистку.
class MyThread(Threading.thread):
def __init__(self):
self.alive=True
def run(self):
while self.alive:
#do stuff
#cleanup goes here, outside the loop
try:
myThread = MyThread()
except KeyboardInterrupt:
myThread.alive = False
myThread.join()
Я обнаружил, что использование библиотеки сигналов python в этом случае очень хорошо работает. Когда вы инициализируете пул, вы можете передать обработчик сигнала в каждый поток, чтобы установить поведение по умолчанию, когда основной поток получает прерывание клавиатуры.
Если вы действительно хотите, чтобы все умирало, поймайте исключение прерывания клавиатуры в основном потоке и вызовите pool.terminate().