2011-05-30 5 views
9

चलाने के लिए डेटा को कैसे पास किया जाए pthread का उपयोग करते समय, मैं थ्रेड निर्माण समय पर डेटा पास कर सकता हूं।थ्रेड

पहले से चल रहे थ्रेड पर नया डेटा पास करने का सही तरीका क्या है?

मैं वैश्विक चर बनाने पर विचार कर रहा हूं और अपना धागा उस से पढ़ता हूं।

धन्यवाद

+7

एक बार फिर, लोग प्रश्न बंद करने की कोशिश कर रहे हैं क्योंकि वे शुरुआती प्रश्न हैं। याद रखने की कोशिश करें कि आप एक बार शुरुआत कर रहे थे। –

उत्तर

6

यह निश्चित रूप से काम करेगा। असल में, धागे केवल हल्के वजन वाली प्रक्रियाएं होती हैं जो समान स्मृति स्थान साझा करती हैं। उस मेमोरी स्पेस में होने वाले वैश्विक चर, प्रत्येक थ्रेड के लिए उपलब्ध हैं।

यह चालक पाठकों के साथ लेखकों के साथ इतना नहीं है। यदि आपके पास वैश्विक स्मृति का एक साधारण हिस्सा है, जैसे int, तो उस int को असाइन करना संभवतः सुरक्षित होगा। बीटी कुछ और जटिल मानते हैं, जैसे struct। बस निश्चित होना करने के लिए, मान लीजिए कि हमारे पास

struct S { int a; float b; } s1, s2; 

अब s1,s2 प्रकार struct S की चर हैं। हम उन्हें

s1 = { 42, 3.14f }; 

प्रारंभ कर सकते हैं और हम उन्हें

s2 = s1; 

असाइन कर सकते हैं लेकिन जब हम उन्हें प्रोसेसर आवंटित एक ही चरण में पूरे struct के काम को पूरा करने के लिए गारंटी नहीं है - हम कहते हैं कि यह है परमाणु नहीं। तो चलो अब दो धागे की कल्पना करते हैं:

thread 1: 
    while (true){ 
     printf("{%d,%f}\n", s2.a, s2.b); 
     sleep(1); 
    } 

thread 2: 
    while(true){ 
     sleep(1); 
     s2 = s1; 
     s1.a += 1; 
     s1.b += 3.14f ; 
    } 

हम देख सकते हैं कि हम मूल्यों {42, 3.14}, {43, 6.28}, {44, 9.42} ....

लेकिन करने के लिए उम्मीद थी s2 क्या हम मुद्रित देखें

{42,3.14} 
{43,3.14} 
{43,6.28} 
ऐसा कुछ हो सकता है

या

{43,3.14} 
{44,6.28} 

और इतने पर। समस्या यह है कि थ्रेड 1 को उस असाइनमेंट के दौरान नियंत्रण प्राप्त हो सकता है और एस 2 को किसी भी समय देखा जा सकता है।

नैतिक यह है कि वैश्विक स्मृति यह करने के लिए एक पूरी तरह से काम करने योग्य तरीका है, आपको इस बात को ध्यान में रखना होगा कि आपके धागे एक दूसरे से अधिक हो जाएंगे। इसके लिए कई समाधान हैं, मूलभूत सेमफोर का उपयोग करने के लिए। एक सेमफोर में दो ऑपरेशन होते हैं, जो डच से पी और वी के रूप में भ्रमित रूप से नामित हैं।

पी बस एक चर 0 तक प्रतीक्षा करता है और आगे बढ़ता है, चर को 1 जोड़ता है; वी चर से 1 घटाता है। विशेष बात यह है कि वे परमाणु रूप से करते हैं - इन्हें बाधित नहीं किया जा सकता है।

अब, आप

thread 1: 
    while (true){ 
     P(); 
     printf("{%d,%f}\n", s2.a, s2.b); 
     V(); 
     sleep(1); 
    } 

thread 2: 
    while(true){ 
     sleep(1); 
     P(); 
     s2 = s1; 
     V(); 
     s1.a += 1; 
     s1.b += 3.14f ; 
    } 

के रूप में कोड करते हैं और आपको इसकी गारंटी रहे हैं आप कभी भी न हो कि धागा 2 आधा पूरा एक काम है, जबकि धागा 1 मुद्रित करने के लिए कोशिश कर रहा है।

(pthreads वैसे, संकेतबाहु है।)

+3

@ चार्ली: क्षमा करें, लेकिन मैं धागे के शब्दों के बारे में "हल्के प्रक्रियाओं" के रूप में पूरी तरह से असहमत हूं। कई ओएस में - कई यूनिक्सॉयड सिस्टम (जैसे बीएसडी) और विंडोज़ - थ्रेड्स वे इकाइयां हैं जो * रन * कोड हैं, जबकि प्रक्रियाएं धागे, मेमोरी स्पेस इत्यादि के लिए केवल कंटेनर हैं। अब, मुझे एहसास है कि यह नाइटपिक हो सकता है, लेकिन मैं लगता है कि भेद बहुत प्रासंगिक और बहुत महत्वपूर्ण है। – 0xC0000022L

+2

@ एसएडी यह ऐतिहासिक और तथ्यात्मक दोनों भी गलत है। प्रक्रियाएं एक के साथ स्मृति रिक्त स्थान हैं - या धागे के साथ, एक से अधिक - प्रोग्राम काउंटर। एक धागा एक "नियंत्रण का धागा" है जिसमें एक कार्यक्रम काउंटर और एक ढेर शामिल है। उदाहरण के लिए धागे पर विकी लेख देखें। http://en.wikipedia.org/wiki/Thread_(computer_science) लोग अब इस भ्रमित हो जाते हैं, लेकिन एक अच्छी ऑपरेटिंग सिस्टम बुक इतिहास पर चर्चा करेगी और इसे साफ़ करेगी। –

+0

यह विचार है कि "धागे केवल हल्के वजन वाली प्रक्रियाएं हैं जो समान स्मृति स्थान साझा करती हैं" वास्तव में लिनक्स पर "पॉज़िक्स धागे" के मूल रूप से खराब, गैर-पॉज़िक्स-अनुपालन कार्यान्वयन से लिखी गई एक भयानक गलतफहमी है, जिसे लिनक्स थ्रेड्स के नाम से जाना जाता है। यह शब्द "प्रक्रिया" और "धागा" के मानक की परिभाषा/उपयोग के लिए पूरी तरह से विरोधाभासी है। –

2

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

2

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

ठेठ दृष्टिकोण अपने धागा निर्माण दिनचर्या मूल रूप से एक घटना पाश बनाने की है। आप एक कतार संरचना बना सकते हैं और थ्रेड निर्माण दिनचर्या के लिए तर्क के रूप में पारित कर सकते हैं। फिर अन्य धागे चीजों को घेर सकते हैं और थ्रेड के इवेंट लूप इसे हटा देंगे और डेटा को संसाधित करेंगे। ध्यान दें कि यह एक वैश्विक चर (या वैश्विक कतार) से क्लीनर है क्योंकि यह इन कतारों में से कई को स्केल कर सकता है।

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

3

मैं संदेश-गुजरने वाले, निर्माता-उपभोक्ता कतार-आधारित, कॉमर्स तंत्र का उपयोग कर रहा हूं, जैसा कि दशकों तक विशेष रूप से बहु-थ्रेडिंग से संबंधित किसी भी समस्या के बिना एवेविक द्वारा सुझाया गया है। कुछ फायदे हैं:

1) कतार पर पारित 'थ्रेडकॉम्स क्लास' उदाहरण अक्सर थ्रेड के लिए आवश्यक काम कर सकते हैं - इनपुट डेटा के लिए सदस्य/एस, आउटपुट डेटा के लिए सदस्य/एस, विधियों के लिए तरीके काम करने के लिए कॉल करने के लिए कॉल करने के लिए कॉल करने के लिए कहीं भी, किसी भी त्रुटि/अपवाद संदेश और एक 'वापसी करने के लिए (इस)' घटना को कॉल करने के लिए कॉल करने के लिए अनुरोध करें ताकि कुछ थ्रेड-सुरक्षित द्वारा अनुरोधकर्ता को सबकुछ वापस कर दिया जाए, जिसका अर्थ है कि कार्यकर्ता धागे को इसके बारे में जानने की आवश्यकता नहीं है। कार्यकर्ता थ्रेड तब पूरी तरह से encapsulated डेटा के एक सेट पर असीमित रूप से चलाता है जिसके लिए कोई लॉकिंग की आवश्यकता नहीं होती है। 'returnToSender (this)' ऑब्जेक्ट को किसी अन्य पी-सी कतार पर कतारबद्ध कर सकता है, यह इसे एक जीयूआई थ्रेड पर पोस्ट कर सकता है, यह ऑब्जेक्ट को पूल में वापस छोड़ सकता है या बस इसे निपटान कर सकता है। जो कुछ भी करता है, कार्यकर्ता थ्रेड को इसके बारे में जानने की आवश्यकता नहीं होती है।

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

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

4) एक थ्रेडपूल लूप के लिए एक-पंक्ति तक कम हो जाता है जो कई कार्य धागे बनाता है और उन्हें एक ही इनपुट कतार पास करता है।

5) लॉकिंग कतारों तक ही सीमित है। अधिक म्यूटेक्स, कोंडवार्स, महत्वपूर्ण-सेक्शन और अन्य सिंच्रो ताले एक ऐप में हैं, जितना मुश्किल है इसे नियंत्रित करना और जितना अधिक असंतुलित डेडलॉक का मौका है जो डीबग करने का दुःस्वप्न है। कतारबद्ध संदेशों के साथ, (आदर्श), केवल कतार वर्ग में ताले हैं। कतार वर्ग को मल्टीपल उत्पादकों/उपभोक्ताओं के साथ 100% काम करना चाहिए, लेकिन यह एक वर्ग है, न कि अनियंत्रित लॉकिंग से भरा ऐप, (हाँ!)।

6) किसी थ्रेड कॉमम्स क्लास को किसी भी समय, कहीं भी, किसी भी थ्रेड में उठाया जा सकता है और एक कतार पर धक्का दिया जा सकता है। अनुरोधकर्ता कोड को सीधे ऐसा करने के लिए भी जरूरी नहीं है, उदाहरण के लिए। लॉगर क्लास विधि के लिए एक कॉल, 'myLogger.logString ("ऑपरेशन सफलतापूर्वक पूरा हुआ");' स्ट्रिंग को कॉमम्स ऑब्जेक्ट में कॉपी कर सकते हैं, इसे थ्रेड पर कतार दें जो लॉग लिखता है और 'तुरंत' लौटाता है। यह लॉगर डेटा थ्रेड पर तब होता है जब इसे लॉग डेटा को संभालने के लिए लॉग डेटा को संभालने के लिए किया जाता है - यह इसे लॉग फ़ाइल में लिख सकता है, यह एक मिनट के बाद मिल सकता है कि लॉग फ़ाइल नेटवर्क समस्या के कारण पहुंच योग्य नहीं है। यह तय कर सकता है कि लॉग फ़ाइल बहुत बड़ी है, इसे संग्रहित करें और दूसरा शुरू करें। यह स्ट्रिंग को डिस्क पर लिख सकता है और उसके बाद टर्मिनल विंडो में डिस्प्ले के लिए जीयूआई थ्रेड पर थ्रेड कॉमम्स क्लास उदाहरण पोस्टमेसेज, जो भी हो। यह लॉग अनुरोध करने वाले धागे से कोई फर्क नहीं पड़ता, जो कि प्रदर्शन पर महत्वपूर्ण प्रभाव के बिना लॉगिंग के लिए बुलाए गए किसी अन्य थ्रेड के रूप में करता है।

7) यदि आपको ओएस के लिए इसे बंद करने के लिए ओएस के लिए वाईइंग के बजाए कतार पर इंतजार करने वाले थ्रेड को मारने की आवश्यकता है, तो बस इसे एक संदेश कतार में बताएं।

वहाँ निश्चित रूप से नुकसान कर रहे हैं:

1), सीधे धागा सदस्यों में डेटा Shoving यह संकेत चलाने के लिए और उसके पूरा होने के लिए समझने के लिए और तेजी से हो जाएगा, यह सोचते हैं कि धागा होने की जरूरत नहीं है आसान है इंतज़ार कर हर बार बनाया गया।

2) वास्तव में एसिंक्रोनस ऑपरेशन, जहां थ्रेड कुछ काम कतारबद्ध है और, कुछ समय बाद, कुछ ईवेंट हैंडलर को कॉल करके इसे वापस लौटाता है जिसे परिणामों को वापस संवाद करना होता है, डेवलपर्स को एकल-थ्रेडेड कोड में इस्तेमाल करना अधिक कठिन होता है और अक्सर राज्य-मशीन प्रकार के डिज़ाइन की आवश्यकता होती है जहां संदर्भ डेटा थ्रेड कॉमम्स क्लास में भेजा जाना चाहिए ताकि परिणाम वापस आने पर सही कार्रवाइयां की जा सकें। यदि कभी-कभार मामला है जहां अनुरोधकर्ता को बस इंतजार करना पड़ता है, तो वह थ्रेड कॉमस्क्लस में एक ईवेंट भेज सकता है जो रिटर्न टॉन्डर विधि द्वारा संकेतित हो जाता है, लेकिन यह पूरा होने के लिए कुछ थ्रेड हैंडल पर बस इंतजार करने से स्पष्ट रूप से अधिक जटिल है।

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

multithreading सुरक्षित हो सकता है और लगभग एक खुशी को बनाए रखने/बढ़ाने के लिए कर रहे हैं कि मापनीय, उच्च प्रदर्शन क्षुधा देने (लगभग :) सकते हैं, लेकिन आप साधारण वैश्विक बंद रखना है - उन्हें टकीला की तरह व्यवहार - त्वरित और अभी के लिए आसान है लेकिन आप बस जानते हैं कि वे कल अपना सिर उड़ा देंगे।

शुभकामनाएं!

मार्टिन