2008-10-15 5 views
23

मेरे पास पृष्ठभूमि में चलने वाला थ्रेड है जो अवरुद्ध फैशन में किसी इनपुट डिवाइस से ईवेंट पढ़ रहा है, अब जब मैं एप्लिकेशन से बाहर निकलता हूं तो मैं थ्रेड को ठीक से साफ़ करना चाहता हूं, लेकिन मैं सिर्फ pthread_join() नहीं चला सकता ब्लॉकिंग आईओ के कारण थ्रेड कभी बाहर नहीं निकलता है।आईओ अवरुद्ध करने पर लटक रहे थ्रेड में कैसे शामिल हो?

मैं उस स्थिति को सही ढंग से कैसे हल करूं? क्या मुझे ब्लॉक को तोड़ने के लिए एक pthread_kill (दाढ़ी, सिगियो) या एक pthread_kill (दाढ़ी, SIGALRM) भेजना चाहिए? क्या इनमें से कोई भी सही सिग्नल है? या क्या इस स्थिति को हल करने का कोई और तरीका है और उस बच्चे के थ्रेड को ब्लॉकिंग पढ़ने से बाहर निकलने दें?

वर्तमान में थोड़ा परेशान है क्योंकि मेरे किसी भी गूगलिंग ने समाधान नहीं बनाया है।

यह लिनक्स पर है और pthreads का उपयोग कर रहा है।

संपादित करें: मैं SIGIO और SIGALRM, साथ थोड़ा के आसपास खेला जब मैं एक संकेत हैंडलर वे अवरुद्ध आईओ को तोड़ने स्थापित नहीं करते, लेकिन जब मैं कंसोल पर एक संदेश देना ("आई/ओ संभव"), लेकिन उस संदेश से बचने के लिए सिग्नल हैंडलर इंस्टॉल करें, वे अब अवरुद्ध आईओ को तोड़ नहीं पाएंगे, इसलिए थ्रेड समाप्त नहीं होता है। तो मैं एक कदम वापस करने के लिए वापस हूँ।

+0

QQQ पुस्तकालयों द्वारा रुचि हो सकती है सही जवाब है, जो दुर्भाग्य से बहुत कम वोट है दिखा रहा है। 'pthread_cancel' आपकी समस्या का समाधान है। –

+0

जब तक धागा अवरुद्ध रहता है, तब तक यह कोई नुकसान नहीं पहुंचा सकता है। मुद्दा यह है कि जब आप चीजों को बंद कर रहे हों तो थ्रेड उठता है। तो फिक्स लाइन के बाद कुछ कोड डालना है जो थ्रेड को थ्रेड को किसी भी चीज़ से रोकता है * अन्य * अगर शटडाउन प्रगति पर है। –

+0

एक समान समस्या और संभावित समाधानों पर चर्चा की गई है: [फाइल डिस्क्रिप्टर और बहुप्रचारित कार्यक्रम] (http://www.ddj.com/hpc-high-performance-computing/212001285) – dmityugov

उत्तर

2

पुराना सवाल जो चीजों के विकास के रूप में एक नया उत्तर प्राप्त कर सकता है और बेहतर धागे में सिग्नल संभालती है, तो एक नई तकनीक अब उपलब्ध है।

लिनक्स कर्नेल 2.6 के बाद से।22, प्रणाली एक नया कार्य signalfd() कहा जाता है जो यूनिक्स संकेतों के एक सेट के लिए एक फ़ाइल वर्णनकर्ता को खोलने के लिए इस्तेमाल किया जा सकता प्रदान करता है (उन है कि एकमुश्त एक प्रक्रिया को मारने के बाहर।)

// defined a set of signals 
sigset_t set; 
sigemptyset(&set); 
sigaddset(&set, SIGUSR1); 
// ... you can add more than one ... 

// prevent the default signal behavior (very important) 
sigprocmask(SIG_BLOCK, &set, nullptr); 

// open a file descriptor using that set of Unix signal 
f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC); 

अब आप poll() उपयोग कर सकते हैं या select() अधिक सामान्य फ़ाइल डिस्क्रिप्टर (सॉकेट, डिस्क पर फ़ाइल इत्यादि) के साथ सिग्नल को सुनने के लिए फ़ंक्शन आप सुन रहे थे।

नॉनब्लॉक महत्वपूर्ण है यदि आप एक लूप चाहते हैं जो सिग्नल और अन्य फ़ाइल डिस्क्रिप्टरों को बार-बार जांच सकता है (यानी यह आपके अन्य फ़ाइल डिस्क्रिप्टर पर भी महत्वपूर्ण है)।

मेरे पास ऐसा कार्यान्वयन है जो (1) टाइमर, (2) सॉकेट, (3) पाइप, (4) यूनिक्स सिग्नल, (5) नियमित फाइलों के साथ काम करता है। दरअसल, वास्तव में किसी भी फाइल डिस्क्रिप्टर प्लस टाइमर।

https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.cpp
https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snapwebsites/snap_communicator.h

तुम भी इस तरह के रूप libevent

2

मुझे लगता है, जैसा कि आपने कहा था, एकमात्र तरीका सिग्नल भेजना होगा और उसके साथ उचित तरीके से निपटना होगा। विकल्प SIGTERM, SIGUSR1, सिग्क्विट, SIGHUP, SIGINT, आदि हो सकते हैं

आप अपने इनपुट डिस्क्रिप्टर पर चयन() का भी उपयोग कर सकते हैं ताकि आप केवल तैयार होने पर ही पढ़ सकें। आप एक सेकंड के साथ चयन() का उपयोग कर सकते हैं, कहें, एक सेकंड और फिर जांचें कि क्या थ्रेड खत्म होना चाहिए या नहीं।

3

एक समाधान जो मुझे हुआ था, आखिरी बार मुझे इस तरह का कोई मुद्दा था, एक फ़ाइल (उदाहरण के लिए एक पाइप) बनाना था जो केवल थ्रेड को अवरुद्ध करने के उद्देश्य से अस्तित्व में था।

विचार मुख्य लूप (या प्रति थ्रेड 1) से एक फ़ाइल बनाने के लिए होगा, जैसा कि टाइमआउट सुझाता है - यह आपको थ्रेड को जागृत करने पर बेहतर नियंत्रण देगा)। फ़ाइल I/O पर अवरुद्ध होने वाले सभी थ्रेड एक फ़ाइल (ओं) का उपयोग करके एक चयन() करते हैं, जिसे वे चालू करने की कोशिश कर रहे हैं, साथ ही साथ मुख्य लूप द्वारा बनाई गई फ़ाइल (पढ़ने के सदस्य के रूप में) फाइल डिस्क्रिप्टर सेट)। यह सभी चयन() कॉल वापसी करना चाहिए।

मुख्य लूप से इस "ईवेंट" को संभालने के लिए कोड को प्रत्येक थ्रेड में जोड़ा जाना आवश्यक होगा।

यदि मुख्य लूप को सभी धागे को जागने की आवश्यकता होती है तो यह या तो फ़ाइल को लिख सकती है या इसे बंद कर सकती है।


मैं यह सुनिश्चित करने के लिए नहीं कह सकता कि यह काम करता है, एक पुनर्गठन के रूप में इसका मतलब है कि इसे गायब करने की आवश्यकता है।

9

आपके select() में एक निश्चित स्थिति पर थ्रेड से बाहर निकलने के लिए, समय-समय पर कम समय हो सकता है, भले ही यह कम हो। मुझे पता है, मतदान बेकार है ...

एक और विकल्प प्रत्येक बच्चे के लिए एक पाइप रखना है और इसे थ्रेड द्वारा देखे गए फाइल डिस्क्रिप्टर की सूची में जोड़ना है। जब आप उस बच्चे से बाहर निकलना चाहते हैं तो माता-पिता से पाइप को एक बाइट भेजें। प्रति थ्रेड पाइप की लागत पर कोई मतदान नहीं।

+1

या आपके पास सभी धागे के लिए एक पाइप हो सकता है, "तैयार" स्थिति को एक फ़ाइल डिस्क्रिप्टर (जब तक यह स्तर ट्रिगर होता है) पर प्रतीक्षा/चयन से कई धागे तक लौटाया जाता है। तो एक ही "हत्यारा" पाइप पर इंतजार कर रहे सभी धागे मरने के लिए अधिसूचना प्राप्त करेंगे। –

6

निर्भर करता है कि यह आईओ के लिए कैसे इंतजार कर रहा है।

यदि थ्रेड "अनइंटरप्टिबल आईओ" स्थिति (शीर्ष पर "डी" के रूप में दिखाया गया है) में है, तो वास्तव में आप इसके बारे में कुछ भी नहीं कर सकते हैं।धागे सामान्य रूप से केवल इस स्थिति को संक्षेप में दर्ज करते हैं, जैसे किसी पृष्ठ को स्वैप करने के लिए प्रतीक्षा करना (या मांग-भारित, उदाहरण के लिए mmap'd फ़ाइल या साझा लाइब्रेरी आदि), हालांकि विफलता (विशेष रूप से एनएफएस सर्वर) का कारण बन सकता है यह उस राज्य में लंबे समय तक रहने के लिए है।

वास्तव में इस "डी" राज्य से बचने का कोई तरीका नहीं है। धागा सिग्नल का जवाब नहीं देगा (आप उन्हें भेज सकते हैं, लेकिन वे कतारबद्ध होंगे)।

यदि यह एक सामान्य आईओ समारोह में इस तरह के रूप में पढ़ा(), लिखना() या चयन() या सर्वेक्षण (जैसे वेटिंग समारोह), संकेत सामान्य रूप से वितरित किया जाएगा।

1

मैं हमेशा " मारता हूं" थ्रेड फ़ंक्शन से संबंधित फ़ंक्शन जो मैं शामिल होने से पहले चलता हूं यह सुनिश्चित करता है कि थ्रेड उचित समय के भीतर जुड़ने योग्य हो। जब कोई थ्रेड IO अवरुद्ध करने का उपयोग करता है तो मैं लॉक को तोड़ने के लिए सिस्टम का उपयोग करने का प्रयास करता हूं। उदाहरण के लिए, जब एक सॉकेट का उपयोग कर रहा मारने कॉल बंद होता है (2) या पास (2) उस पर नेटवर्क स्टैक यह सफाई से समाप्त करने के लिए कारण होगा जो।

लिनक्स सॉकेट कार्यान्वयन थ्रेड सुरक्षित है।

0

सिग्नल और थ्रेड लिनक्स पर अलग-अलग मैन पेजों के अनुसार एक सूक्ष्म समस्या है। क्या आप लिनक्स थ्रेड, या एनपीटीएल (यदि आप लिनक्स पर हैं) का उपयोग करते हैं?

मैं इस बारे में सुनिश्चित नहीं हूँ, लेकिन मैं संकेत हैंडलर पूरी प्रक्रिया को प्रभावित करता है लगता है, तो या तो आप अपनी पूरी प्रक्रिया या सब कुछ जारी रखने के लिए समाप्त।

आप समय का उपयोग का चयन करें या सर्वेक्षण, और अपने धागा समाप्त करने के लिए एक वैश्विक ध्वज स्थापित करना चाहिए।

13

मैं भी आपके थ्रेड को समाप्त करने के लिए चुनिंदा या कुछ अन्य गैर सिग्नल-आधारित माध्यमों का उपयोग करने की अनुशंसा करता हूं। धागे के कारणों में से एक कारण सिग्नल पागलपन से कोशिश करना और दूर जाना है। यही कारण है कि ने कहा ...

आम तौर पर एक SIGUSR1 या SIGUSR2 साथ pthread_kill() का उपयोग करता है धागा के लिए एक संकेत भेजने के लिए। अन्य सुझाए गए संकेत - सिगरम, सिगिनट, सिगकिल - में प्रक्रिया-व्यापी अर्थशास्त्र है जिसमें आपको कोई दिलचस्पी नहीं हो सकती है।

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

रसीद आम तौर पर एक पढ़ने से बाहर टूट जाएगा EINTR साथ जब तक यह है कि अबाधित राज्य में सही मायने में है के रूप में पहले के एक जवाब में बताया गया। लेकिन मुझे लगता है कि यह नहीं है, या SIGALRM और SIGIO के साथ आपके प्रयोगों ने प्रक्रिया को समाप्त नहीं किया होगा।

क्या आपका शायद कुछ प्रकार के लूप में पढ़ा जाता है? यदि रीड -1 रिटर्न के साथ समाप्त हो जाता है, तो उस लूप से बाहर निकलें और थ्रेड से बाहर निकलें।

आप यह बहुत ही खराब कोड मैं एक साथ रखा मेरी मान्यताओं का परीक्षण करने के साथ खेल सकते हैं - मैं समय-क्षेत्र इस समय मेरी POSIX किताबों से दूर के एक जोड़े हूँ ...

#include <stdlib.h> 
#include <stdio.h> 
#include <pthread.h> 
#include <signal.h> 

int global_gotsig = 0; 

void *gotsig(int sig, siginfo_t *info, void *ucontext) 
{ 
     global_gotsig++; 
     return NULL; 
} 

void *reader(void *arg) 
{ 
     char buf[32]; 
     int i; 
     int hdlsig = (int)arg; 

     struct sigaction sa; 
     sa.sa_handler = NULL; 
     sa.sa_sigaction = gotsig; 
     sa.sa_flags = SA_SIGINFO; 
     sigemptyset(&sa.sa_mask); 

     if (sigaction(hdlsig, &sa, NULL) < 0) { 
       perror("sigaction"); 
       return (void *)-1; 
     } 
     i = read(fileno(stdin), buf, 32); 
     if (i < 0) { 
       perror("read"); 
     } else { 
       printf("Read %d bytes\n", i); 
     } 
     return (void *)i; 
} 

main(int argc, char **argv) 
{ 
     pthread_t tid1; 
     void *ret; 
     int i; 
     int sig = SIGUSR1; 

     if (argc == 2) sig = atoi(argv[1]); 
     printf("Using sig %d\n", sig); 

     if (pthread_create(&tid1, NULL, reader, (void *)sig)) { 
       perror("pthread_create"); 
       exit(1); 
     } 
     sleep(5); 
     printf("killing thread\n"); 
     pthread_kill(tid1, sig); 
     i = pthread_join(tid1, &ret); 
     if (i < 0) 
       perror("pthread_join"); 
     else 
       printf("thread returned %ld\n", (long)ret); 
     printf("Got sig? %d\n", global_gotsig); 

} 
+0

आप सही हैं, पढ़ा() वास्तव में एक समय लूप में है जो ईआईएनटीआर की जांच करता है, क्योंकि यह तीसरे पक्ष की लाइब्रेरी में है, न कि मेरा खुद का कोड, मैं पूरी तरह से उस तथ्य को याद करता हूं और यही वजह है कि एक साधारण सिग्नल ' मैं क्या उम्मीद कर रहा हूँ। – Grumbel

+0

क्या इस विधि का उपयोग कर फ़्लॉकफाइल के साथ अधिग्रहित संसाधनों को जारी करना संभव है? – Ynv

0

मैं साफ लगता है दृष्टिकोण जारी रखने के लिए एक लूप में सशर्त चर का उपयोग कर धागा होगा।

जब कोई आई/ओ घटना निकाल दी जाती है, तो सशर्त संकेत दिया जाना चाहिए।

मुख्य धागा लूप को झूठ बोलने के दौरान स्थिति को सिग्नल कर सकता है।

कुछ की तरह:

while (!_finished) 
{ 
    pthread_cond_wait(&cond); 
    handleio(); 
} 
cleanup(); 

सशर्त चर ठीक से संकेत संभाल करने के साथ याद रखें। उनके पास 'नकली wakeups' जैसी चीजें हो सकती हैं। तो मैं cond_wait फ़ंक्शन के आस-पास अपना स्वयं का फ़ंक्शन लपेटूंगा।

0
struct pollfd pfd; 
pfd.fd = socket; 
pfd.events = POLLIN | POLLHUP | POLLERR; 
pthread_lock(&lock); 
while(thread_alive) 
{ 
    int ret = poll(&pfd, 1, 100); 
    if(ret == 1) 
    { 
     //handle IO 
    } 
    else 
    { 
     pthread_cond_timedwait(&lock, &cond, 100); 
    } 
} 
pthread_unlock(&lock); 

thread_alive एक धागा विशिष्ट चर कि धागा को मारने के लिए संकेत के साथ संयोजन में इस्तेमाल किया जा सकता है।

हैंडल IO अनुभाग के लिए आपको यह सुनिश्चित करने की आवश्यकता है कि आपने O_NOBLOCK विकल्प के साथ खुलासा किया है, या यदि इसकी सॉकेट एक समान ध्वज है तो आप MSG_NOWAIT सेट कर सकते हैं ?? अन्य fds के लिए मुझे यकीन नहीं है

1

मुझे आश्चर्य है कि किसी ने भी pthread_cancel का सुझाव नहीं दिया है। मैंने हाल ही में एक बहु थ्रेडेड I/O प्रोग्राम लिखा है और रद्द करना() और शामिल() बाद में बहुत अच्छा काम किया है।

मैंने मूल रूप से pthread_kill() की कोशिश की थी लेकिन मैंने पूरे कार्यक्रम को केवल सिग्नल के साथ समाप्त करने के साथ समाप्त कर दिया था।

1

यदि आप किसी तीसरे पक्ष की लाइब्रेरी में अवरुद्ध हो रहे हैं जो ईआईएनटीआर पर लूप करता है, तो आप वास्तव में बंद/प्रतिस्थापन के साथ एक खाली फ़ंक्शन (SIG_IGN नहीं) को कॉल करने वाले सिग्नल (यूएसआर 1 आदि) के साथ pthread_kill का उपयोग करने के संयोजन पर विचार करना चाहेंगे। प्रश्न में फाइल डिस्क्रिप्टर।/Dev/null या इसी तरह के fd को प्रतिस्थापित करने के लिए dup2 का उपयोग करके, आप तृतीय-पक्ष लाइब्रेरी को रीड्री रीट्रीज़ करते समय एंड-ऑफ-फ़ाइल परिणाम प्राप्त करने का कारण बनेंगे।

ध्यान दें कि मूल सॉकेट को पहले डुप्लिकेट करके, आप सॉकेट को वास्तव में बंद करने की आवश्यकता से बच सकते हैं।

12

यह करने के लिए विहित रास्ता pthread_cancel, जहां धागा pthread_cleanup_push/pop किया है किसी भी संसाधनों यह उपयोग कर रहा है के लिए सफाई प्रदान करने के लिए के साथ है।

दुर्भाग्य से इसका उपयोग कभी भी C++ कोड में नहीं किया जा सकता है। pthread_cancel के समय कॉलिंग स्टैक पर कोई भी सी ++ एसडीडी लिब कोड, या कोईसंभावित रूप से आपकी पूरी प्रक्रिया को मारने से बचाएगा।

केवल वैकल्पिक हल SIGUSR1 को संभालने के लिए, एक को रोकने झंडा, pthread_kill(SIGUSR1), तो कहीं धागा मैं पर अवरुद्ध है की स्थापना है/हे, आप थोड़ी देर बाद प्रयास EINTR जांच बंद झंडा प्राप्त करता है, तो मैं/हे। व्यवहार में, यह हमेशा लिनक्स पर सफल नहीं होता है, क्यों नहीं पता क्यों।

लेकिन किसी भी मामले में यह बात करने के लिए बेकार है कि आपको किसी तीसरे पक्ष के lib को कॉल करना है, क्योंकि उनके पास सबसे अधिक संभावना है कि मैं 0/पर I/O को पुनरारंभ करता हूं। रिवर्स इंजीनियरिंग को बंद करने के लिए उनके फ़ाइल डिस्क्रिप्टर को या तो इसे काट नहीं देगा-वे एक सेमफोर या अन्य संसाधन पर इंतजार कर सकते हैं। इस मामले में, काम कोड, अवधि लिखना असंभव है। हाँ, यह पूरी तरह से मस्तिष्क क्षतिग्रस्त है। उन लोगों से बात करें जिन्होंने सी ++ अपवाद और pthread_cancel डिज़ाइन किया था। माना जाता है कि यह सी ++ के कुछ भविष्य के संस्करण में तय किया जा सकता है। उसके साथ अच्छा भाग्य।

संबंधित मुद्दे