1. ホーム
  2. python

[解決済み] PythonでCtrl+C / SIGINTをキャッチしてマルチプロセスを優雅に終了させる [重複] [重複

2023-04-09 15:28:03

質問

をキャッチするにはどうすればよいのでしょうか? Ctrl + C をマルチプロセスPythonプログラムで実行し、すべてのプロセスを優雅に終了するために、私はunixと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 / SIGINTmultiprocessing.Pool にすることです。

  1. プロセスを無視するようにする SIGINT プロセスの前に Pool が作成されます。このようにして作成された子プロセスは SIGINT ハンドラを継承する。
  2. 元に戻す SIGINT ハンドラの後、親プロセスで Pool が作成された後、親プロセスの
  3. 使用する map_asyncapply_async をブロックするのではなく mapapply .
  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 を使用して、親プロセスでのシグナルの配信を一時的にブロックすれば、シグナルの消失を防ぐことができますが、Python-2 では利用することができません。