91

CtrlマルチプロセスPythonプログラムで+をキャッチし、すべてのプロセスを正常に終了するにはどうすればよいですかC。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()

そして、それは一種の機能ですが、私はそれが正しい解決策ではないと思います。

4

3 に答える 3

89

以前に受け入れられたソリューションmapには競合状態があり、機能しませんasync


Ctrl+C/SIGINTを処理する正しい方法multiprocessing.Poolは次のとおりです。

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

于 2016-02-01T15:33:35.283 に答える
37

解決策はこのリンクこのリンクに基づいており、問題を解決しましたが、次の場所に移動する必要Poolがありました:

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()
于 2012-07-03T14:20:50.037 に答える