2009-03-10 7 views
22

में इंटरप्टिबल थ्रेड शामिल हो गया है क्या थ्रेड को समाप्त करने का इंतजार करने का कोई तरीका है, लेकिन अभी भी संकेतों को रोकना है?पाइथन

निम्नलिखित सी कार्यक्रम पर विचार करें:

#include <signal.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <pthread.h> 
#include <stdlib.h> 

void* server_thread(void* dummy) { 
    sleep(10); 
    printf("Served\n"); 
    return NULL; 
} 

void* kill_thread(void* dummy) { 
    sleep(1); // Let the main thread join 
    printf("Killing\n"); 
    kill(getpid(), SIGUSR1); 
    return NULL; 
} 

void handler(int signum) { 
    printf("Handling %d\n", signum); 
    exit(42); 
} 

int main() { 
    pthread_t servth; 
    pthread_t killth; 

    signal(SIGUSR1, handler); 

    pthread_create(&servth, NULL, server_thread, NULL); 
    pthread_create(&killth, NULL, kill_thread, NULL); 

    pthread_join(servth, NULL); 

    printf("Main thread finished\n"); 
    return 0; 
} 

यह एक के बाद एक दूसरा और प्रिंट समाप्त होता है:

:

Killing 
Handling 10 

इसके विपरीत, यहां में यह लिखने के लिए अजगर मेरी प्रयास है

#!/usr/bin/env python 
import signal, time, threading, os, sys 

def handler(signum, frame): 
    print("Handling " + str(signum) + ", frame:" + str(frame)) 
    exit(42) 
signal.signal(signal.SIGUSR1, handler) 

def server_thread(): 
    time.sleep(10) 
    print("Served") 
servth = threading.Thread(target=server_thread) 
servth.start() 

def kill_thread(): 
    time.sleep(1) # Let the main thread join 
    print("Killing") 
    os.kill(os.getpid(), signal.SIGUSR1) 
killth = threading.Thread(target=kill_thread) 
killth.start() 

servth.join() 

print("Main thread finished") 

यह प्रिंट:

Killing 
Served 
Handling 10, frame:<frame object at 0x12649c0> 

मैं इसे कैसे सी संस्करण की तरह व्यवहार कर सकता हूँ?

+0

'जीसीसी -pthread thread.c' तरह से करता है, तो किसी को भी तरह त्रुटियों का सामना करना पड़ा सी स्रोत संकलित करने के लिए है मैं अकेले 'gcc thread.c' कोशिश कर रहा हूं। – ViFI

उत्तर

5

Jarret हार्डी पहले से ही mentioned it: Guido van Rossum के अनुसार, वहाँ अब तक कोई बेहतर तरीका है: के रूप में documentation, join(None) ब्लॉक में कहा गया है (और इसका मतलब है कि कोई संकेत)। वैकल्पिक - एक विशाल टाइमआउट (join(2**31) या तो) के साथ कॉल करना और isAlive जांचना बहुत अच्छा लग रहा है। हालांकि, जिस तरह से अजगर टाइमर संभालती विनाशकारी, के रूप में देखा जब servth.join(100)servth.join() के बजाय साथ अजगर परीक्षण कार्यक्रम चल रहा है:

select(0, NULL, NULL, NULL, {0, 1000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 2000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 4000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 8000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 16000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 32000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 50000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 50000}) = 0 (Timeout) 
select(0, NULL, NULL, NULL, {0, 50000}) = 0 (Timeout) 
--- Skipped 15 equal lines --- 
select(0, NULL, NULL, NULL, {0, 50000}Killing 

Ie, अजगर हर 50 एमएस जाग, एक भी आवेदन को निष्क्रिय होने से सीपीयू रखने के लिए अग्रणी ।

+0

पायथन 3 में, 'servth.join() '[lock.acquire()'] पर अवरोधित है (https://docs.python.org/3/library/_thread.html#_thread.lock.acquire) हो सकता है एक संकेत से बाधित – jfs

+0

मैं @ एफहाग (पायथन 2.6 सेंट -6 में) के साथ मिलकर पोस्ट कर रहा हूं: जब मैं अपना मुख्य धागा थ्रेड करता हूं तो थ्रू .is_alive(): thread.join() '... लेकिन दुर्भाग्य से मैं संकेत करता हूं। सिग्नल प्राप्त नहीं किया। अजीब बात यह है कि जब मैं थ्रेड करता हूं 'thread.is_alive(): thread.join (30.0)' मुझे अपेक्षित संकेत मिलता है। -> ** संक्षेप में मुझे वही व्यवहार मिला जो @ एफहाग मिला (यानी आपको टाइमआउट के साथ 'thread.join' का उपयोग करना होगा ... अन्यथा आप सिग्नल प्राप्त नहीं कर सकते हैं)। ** –

+0

यहां [एक और जवाब ] (https://stackoverflow.com/a/29661280/52074) जो कि आपको टाइमआउट –

3

join पर कॉल करने से पहले isAlive पर मतदान। यह मतदान निश्चित रूप से बाधित हो सकता है, और एक बार धागा isAlive नहीं है, join तत्काल है।

एक विकल्प समय के साथ join पर मतदान करेगा, isAlive के साथ जांच कर रहा है कि समय सीमा हुई है। यह पिछले विधि की तुलना में कम CPU खर्च कर सकते हैं।

+1

निश्चित रूप से, मतदान कार्य करता है, लेकिन यह अधिक संसाधनों का उपयोग करता है और सीपीयू नींद की स्थिति को रोकता है जो नोटबुक पर काफी महंगा हो सकता है। मैं एक और समाधान की तलाश में हूं। – phihag

+0

हां, लेकिन दूसरी विधि का उपयोग करके आप CPU को बर्बाद नहीं करते हैं, क्योंकि टाइमआउट ब्लॉक के साथ जुड़ें और इसे रिलीज़ करें। तो कुछ दर्जनों मिलीसेकंड के अपेक्षाकृत छोटे टाइमआउट आपको 99.9% सीपीयू मुक्त –

+0

एलिबेन छोड़ देंगे: 99.9% सीपीयू फ्री कम से कम वांछनीय नहीं है अगर काम समान रूप से फैलता है, तो मैं 80% सीपीयू मुक्त पसंद करता हूं डेस्कटॉप एप्लिकेशन के लिए एक ही विस्फोट में। विवरण के लिए http://www.lesswatts.org/projects/applications-power-management/avoid-pulling.php देखें। – phihag

14

पाइथन में थ्रेड कुछ हद तक अजीब जानवर हैं जो वैश्विक दुभाषिया ताला देते हैं। आप समय-समय पर शामिल होने के बिना जो भी चाहते हैं उसे प्राप्त करने में सक्षम नहीं हो सकते हैं और एलिबिन सुझाव देते हैं।

दस्तावेज़ों में दो धब्बे हैं जो इस (और संभवतः अधिक) के कारण देते हैं।

पहले:

http://docs.python.org/library/signal.html#module-signal से:

कुछ देखभाल करता है, तो दोनों संकेतों और धागे एक ही कार्यक्रम में किया जाता है लिया जाना चाहिए। के लिए मौलिक बात सिग्नल और थ्रेड का उपयोग करने में याद रखें: साथ ही निष्पादन के मुख्य थ्रेड में सिग्नल() संचालन करें। कोई भी थ्रेड एक अलार्म(), getignal(), रोकें(), setitimer() या getitimer(); केवल मुख्य थ्रेड एक नया संकेत हैंडलर सेट कर सकते हैं, और मुख्य थ्रेड संकेतों को प्राप्त करने के लिए केवल एक ही (इस अजगर संकेत मॉड्यूल द्वारा लागू की जाती, अंतर्निहित धागा कार्यान्वयन के लिए संकेत भेजने का समर्थन करता है, भले ही हो जाएगा व्यक्तिगत धागे)। यह का अर्थ है कि सिग्नल का उपयोग इंटर-थ्रेड संचार के माध्यम से नहीं किया जा सकता है। इसके बजाय ताले का उपयोग करें।

दूसरा, http://docs.python.org/library/thread.html#module-thread से:

धागे बीच में आता है के साथ अजीब बातचीत: KeyboardInterrupt अपवाद एक मनमाना धागा द्वारा प्राप्त किया जाएगा। (जब संकेत मॉड्यूल उपलब्ध है, व्यवधान हमेशा मुख्य थ्रेड पर जाने।)

संपादित करें: वहाँ अजगर बग ट्रैकर यहाँ पर इस बात का यांत्रिकी के एक सभ्य चर्चा नहीं हुई: http://bugs.python.org/issue1167930। बेशक, यह गिडो के साथ समाप्त होता है: "यह दूर जाने की संभावना नहीं है, इसलिए आपको इसके साथ रहना होगा। जैसा कि आपने पाया है, एक टाइमआउट निर्दिष्ट करने से समस्या (सॉर्ट) हल हो जाती है।" YMMV :-)

+0

ठीक है, मैं * मुख्य थ्रेड (1) में सिग्नल.signal को कॉल कर रहा हूं, और सिग्नल मॉड्यूल उपलब्ध है (2)। – phihag

+0

दाएं, लेकिन सिग्नल केवल मुख्य धागे पर जाएगा, इसलिए सिग्नल हैंडलर (मुख्य धागे के माध्यम से) सिग्नल पर जाने से पहले आपको सर्वथ में शामिल होने की प्रतीक्षा करनी होगी। उलझन में, नहीं? –

0

मुझे पता है कि मैं पार्टी के लिए थोड़ा देर हो चुकी हूं, लेकिन मैं इस प्रश्न पर आया कि एक टाइमआउट के साथ जुड़ने से बेहतर जवाब मिलने की उम्मीद है, जो मैं पहले से कर रहा था। अंत में मैं कुछ है कि या संकेतों के एक भयानक bastardisation नहीं हो सकता पकाया, लेकिन यह जब धागा इसके निष्पादन के अंत तक पहुँच जाता है signal.pause() बजाय Thread.join() का उपयोग करते हुए और वर्तमान प्रक्रिया संकेत शामिल है:

import signal, os, time, sys, threading, random 

threadcount = 200 

threadlock = threading.Lock() 
pid = os.getpid() 
sigchld_count = 0 

def handle_sigterm(signalnum, frame): 
    print "SIGTERM" 

def handle_sigchld(signalnum, frame): 
    global sigchld_count 
    sigchld_count += 1 

def faux_join(): 
    global threadcount, threadlock 
    threadlock.acquire() 
    threadcount -= 1 
    threadlock.release() 
    os.kill(pid, signal.SIGCHLD) 

def thread_doer(): 
    time.sleep(2+(2*random.random())) 
    faux_join() 

if __name__ == '__main__': 
    signal.signal(signal.SIGCHLD, handle_sigchld) 
    signal.signal(signal.SIGTERM, handle_sigterm) 

    print pid 
    for i in xrange(0, threadcount): 
     t = threading.Thread(target=thread_doer) 
     t.start() 

    while 1: 
     if threadcount == 0: break 
     signal.pause() 
     print "Signal unpaused, thread count %s" % threadcount 

    print "All threads finished" 
    print "SIGCHLD handler called %s times" % sigchld_count 

हैं आप SIGTERMs को क्रिया में देखना चाहते हैं, thread_doer में नींद के समय की लंबाई बढ़ाएं और kill $pid अन्य टर्मिनल से कमांड जारी करें, जहां $pid प्रारंभ में मुद्रित पिड आईडी है।

मैं इसे दूसरों की मदद करने की आशा में जितना अधिक पोस्ट करता हूं, यह बताया जाता है कि यह पागल है या एक बग है। मुझे यकीन नहीं है कि थ्रेडकाउंट पर लॉक अभी भी जरूरी है - मैंने इसे अपने प्रयोग में जल्दी रखा और सोचा कि मुझे इसे मामले में वहां छोड़ देना चाहिए।

1

जहाँ तक मैं समझता हूँ के रूप में, एक समान प्रश्न The Little Book of Semaphores (मुफ्त डाउनलोड) में हल किया जाता है, परिशिष्ट A भाग 3 ...

+0

के साथ एक थ्रेड में शामिल होना है, हालांकि यह एक हैक है, अनिवार्य रूप से अंतर-प्रक्रिया संचार के साथ अंतर – phihag