2012-07-03 17 views
59

में Ctrl + C/SIGINT और बाहर निकलें multiprocesses को पकड़ें मैं मल्टीप्रोसेस पायथन प्रोग्राम में Ctrl + C कैसे पकड़ूं और सभी प्रक्रियाओं से बाहर निकलता हूं, मुझे यूनिक्स और विंडोज दोनों पर काम करने का समाधान चाहिए। मैं निम्नलिखित की कोशिश की है:पाइथन

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

और यह एक तरह से काम कर रहा है, लेकिन मैं इसे सही समाधान है नहीं लगता।

संपादित करें: यह this one

+1

[पाइथन के मल्टीप्रोसेसिंग पूल के साथ कीबोर्ड इंटरप्ट्स] का संभावित डुप्लिकेट (https://stackoverflow.com/questions/1408356/keyboard-interrupts-with-pythons-multiprocessing-pool) – tabata

उत्तर

37

The previously accepted solution में दौड़ की स्थिति है और यह map और async फ़ंक्शंस के साथ काम नहीं करता है।

को संभालने के लिए सही तरीका Ctrl + C/SIGINTmultiprocessing.Pool साथ करने के लिए है:

  1. प्रक्रिया एक प्रक्रिया Pool बनाई गई है पहले SIGINT उपेक्षा करें। इस तरह बाल प्रक्रियाओं को SIGINT हैंडलर का उत्तराधिकारी बनाया गया।
  2. Pool के बाद मूल प्रक्रिया में मूल SIGINT हैंडलर को पुनर्स्थापित करें।
  3. उपयोग map_async और बदले map और apply अवरुद्ध apply_async
  4. टाइमआउट के साथ परिणामों पर प्रतीक्षा करें क्योंकि डिफ़ॉल्ट अवरोध सभी संकेतों को अनदेखा करता है। यह पायथन बग 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 में उपलब्ध नहीं है।

+0

यह उत्तर विंडोज़ में पाइथन 2.7.11 के साथ काम करता है हालांकि एक सिगिनट एक लंबी ट्रेसबैक फेंकता है। 3.5.2 में यह उत्तर काम नहीं करता है - SIGINT कभी भी ठीक से बाहर निकलता नहीं है। इसके बजाय आपको 'कीबोर्ड इंटरप्ट' और 'प्रोसेस स्पॉनपूलवर्कर' संदेश और मल्टीप्रोसेसिंग मॉड्यूल से ट्रेसबैक मिलते हैं और आपको मैन्युअल रूप से स्पॉन्ड प्रक्रियाओं को मारना होगा। स्वीकृत उत्तर इस व्यवहार को प्रदर्शित नहीं करता है। – MartyMacGyver

+0

@MartyMacGyver केवल Python 2.7.x के साथ इसका परीक्षण किया। –

+0

पाइथन 2.7.11 के साथ लिनक्स (उबंटू 16.04) पर भी काम करता है। यह वास्तव में स्वीकार्य जवाब होना चाहिए। – theV0ID

33

का डुप्लिकेट समाधान this link और this link पर आधारित है हो सकता है और यह समस्या हल हो, मैं करने के लिए 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() 
+0

यह बहुत देर हो चुकी है: एक दौड़ है बाल प्रक्रिया में 'फोर्क()' रिटर्न और 'सिग्नल()' कॉल के बीच की स्थिति खिड़की। फोर्किंग से पहले सिग्नल अवरुद्ध होना चाहिए। –

+1

@MaximYegorushkin - सिग्नल 'init_worker' में अवरुद्ध है जिसे 'apply_async' से पहले कहा जाता है - क्या आप इसके बारे में बात कर रहे हैं? – zenpoy

+0

मेरा मतलब यह है कि बच्चे की प्रक्रिया को फोर्क और अनब्लॉक करने से पहले सिग्नल को अवरुद्ध किया जाना चाहिए। इस तरह बच्चे को सिग्नल मास्क प्राप्त होता है और सिग्नल प्राप्त करने का कोई मौका नहीं होता है। –

11
बस

अपने कार्यकर्ता प्रक्रिया में कीबोर्ड इंटरप्ट-सिस्टम एक्सीट अपवादों को संभालें:

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

सिग्नल के लिए जो पायथन सिस्टमएक्सिट बढ़ाते हैं, यह वास्तव में पाइथन 3.6 पर भी काम करता है। मुझे आश्चर्य है कि, इसमें क्या सिग्नल शामिल हैं? मुझे लगता है कि सिगिल और सिगरम ...? – Petri

+1

आप आसानी से जांच सकते हैं कि कौन से संकेत शामिल हैं और उत्तर है: मुझे कोई नहीं लगता। SystemExit केवल दस्तावेज़ों के अनुसार sys.exit द्वारा उठाया जाता है। बस निष्पादन करें * time.sleep (60) बेसएक्सप्शन को छोड़कर ई: प्रिंट (ई) 'और आप देखेंगे कि कोई विशिष्ट सिग्नल पकड़ा गया है (केवल सिगिनट)। मैनपेज भी यही कहता है। –