Убить многопроцессорный пул Python

Я запускаю программу Python, которая использует модуль многопроцессорности для создания некоторых рабочих потоков. Используя Pool.map, они перескажут список файлов.

В какой-то момент я хотел бы остановить все и умереть script.

Обычно Ctrl+C из командной строки выполняет это. Но, в данном случае, я думаю, что просто прерывает одного из рабочих и создается новый рабочий.

Итак, я заканчиваю запуск ps aux | grep -i python и используя kill -9 для рассматриваемых идентификаторов процесса.

Есть ли лучший способ, чтобы сигнал прерывания приводил все к остановке?

Ответ 1

SIGQUIT (Ctrl + \) убьет все процессы даже в Python 2.x.

Вы также можете обновить до Python 3.x, где это поведение (только дочерний сигнал получает сигнал), кажется, исправлено.

Ответ 2

К сожалению, в 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).

Ответ 3

Есть несколько способов. Первый способ - отметить поток как демона, используя

в 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()

Ответ 4

Я обнаружил, что использование библиотеки сигналов python в этом случае очень хорошо работает. Когда вы инициализируете пул, вы можете передать обработчик сигнала в каждый поток, чтобы установить поведение по умолчанию, когда основной поток получает прерывание клавиатуры.

Если вы действительно хотите, чтобы все умирало, поймайте исключение прерывания клавиатуры в основном потоке и вызовите pool.terminate().