IT박스

Ctrl + C / SIGINT를 잡아 파이썬에서 우아하게 다중 프로세스를 종료합니다.

itboxs 2020. 10. 17. 10:05
반응형

Ctrl + C / SIGINT를 잡아 파이썬에서 우아하게 다중 프로세스를 종료합니다.


다중 프로세스 파이썬 프로그램에서 Ctrl + C를 잡고 모든 프로세스를 정상적으로 종료하려면 어떻게해야합니까? 유닉스와 Windows 모두에서 작동하는 솔루션이 필요합니다. 나는 다음을 시도했다 :

import multiprocessing
import time
import signal
import sys

jobs = []

def worker():
    signal.signal(signal.SIGINT, signal_handler)
    while(True):
        time.sleep(1.1234)
        print "Working..."

def signal_handler(signal, frame):
    print 'You pressed Ctrl+C!'
    # for p in jobs:
    #     p.terminate()
    sys.exit(0)

if __name__ == "__main__":
    for i in range(50):
        p = multiprocessing.Process(target=worker)
        jobs.append(p)
        p.start()

그리고 그것은 일종의 효과가 있지만 올바른 해결책이라고 생각하지 않습니다.

편집 : 이의 중복 될 수 이것


이전에 승인 솔루션은 경쟁 조건을 가지고 있으며, 그것은 작동하지 않습니다 mapasync기능.

Ctrl + C를 처리하는 올바른 방법은 / SIGINT로는 multiprocessing.Pool에 있습니다 :

  1. SIGINT프로세스 Pool가 생성 되기 전에 프로세스를 무시 합니다. 이렇게 생성 된 자식 프로세스는 SIGINT처리기를 상속 합니다.
  2. 가 생성 된 SIGINT후 부모 프로세스에서 원래 처리기를 복원합니다 Pool.
  3. 사용 map_async하고 apply_async대신에 차단 map하고 apply.
  4. 기본 차단 대기는 모든 신호를 무시하므로 시간 초과와 함께 결과를 기다립니다. 이것은 Python 버그 https://bugs.python.org/issue8296 입니다.

종합 :

#!/bin/env python
from __future__ import print_function

import multiprocessing
import os
import signal
import time

def run_worker(delay):
    print("In a worker process", os.getpid())
    time.sleep(delay)

def main():
    print("Initializng 2 workers")
    original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
    pool = multiprocessing.Pool(2)
    signal.signal(signal.SIGINT, original_sigint_handler)
    try:
        print("Starting 2 jobs of 5 seconds each")
        res = pool.map_async(run_worker, [5, 5])
        print("Waiting for results")
        res.get(60) # Without the timeout this blocking call ignores all signals.
    except KeyboardInterrupt:
        print("Caught KeyboardInterrupt, terminating workers")
        pool.terminate()
    else:
        print("Normal termination")
        pool.close()
    pool.join()

if __name__ == "__main__":
    main()

@YakovShklarov가 언급했듯이 신호를 무시하고 부모 프로세스에서 신호를 무시하지 않는 사이에는 신호가 손실 될 수있는 기간이 있습니다. 사용하여 pthread_sigmask일시적으로 손실되는 신호를 방해하는 부모 프로세스에서 신호의 전달을 차단하는 대신에, 그러나, 파이썬-2에서 사용할 수 없습니다.


The solution is based on this link and this link and it solved the problem, I had to moved to Pool though:

import multiprocessing
import time
import signal
import sys

def init_worker():
    signal.signal(signal.SIGINT, signal.SIG_IGN)

def worker():
    while(True):
        time.sleep(1.1234)
        print "Working..."

if __name__ == "__main__":
    pool = multiprocessing.Pool(50, init_worker)
    try:
        for i in range(50):
            pool.apply_async(worker)

        time.sleep(10)
        pool.close()
        pool.join()

    except KeyboardInterrupt:
        print "Caught KeyboardInterrupt, terminating workers"
        pool.terminate()
        pool.join()

Just handle KeyboardInterrupt-SystemExit exceptions in your worker process:

def worker():
    while(True):
        try:
            msg = self.msg_queue.get()
        except (KeyboardInterrupt, SystemExit):
            print("Exiting...")
            break

참고URL : https://stackoverflow.com/questions/11312525/catch-ctrlc-sigint-and-exit-multiprocesses-gracefully-in-python

반응형