एक परियोजना से आपने क्या सबक सीखा जो लगभग / वास्तव में खराब मल्टीथ्रेडिंग के कारण विफल रहा? [बन्द है]


11

एक परियोजना से आपने क्या सबक सीखा जो लगभग / वास्तव में खराब मल्टीथ्रेडिंग के कारण विफल रहा?

कभी-कभी, फ्रेमवर्क एक निश्चित थ्रेडिंग मॉडल को लागू करता है जो चीजों को सही होने के लिए अधिक परिमाण का एक आदेश बनाता है।

मेरे लिए, मुझे अभी तक अंतिम असफलता से उबरना है और मुझे लगता है कि मेरे लिए बेहतर है कि मैं उस ढांचे में मल्टीथ्रेडिंग के साथ काम न करूं।

मैंने पाया कि मैं मल्टीथ्रेडिंग समस्याओं में अच्छा था, जिनमें सरल कांटा / जुड़ाव है, और जहां डेटा केवल एक दिशा में यात्रा करता है (जबकि सिग्नल एक गोलाकार दिशा में यात्रा कर सकते हैं)।

मैं GUI को संभालने में असमर्थ हूं, जिसमें कुछ काम केवल एक कड़ाई से अनुक्रमित धागे ("मुख्य धागा") पर किया जा सकता है और अन्य काम केवल किसी भी धागे पर किया जा सकता है, लेकिन मुख्य धागा ("कार्यकर्ता सूत्र"), और जहां डेटा और संदेशों को एन घटकों (एक पूरी तरह से जुड़ा हुआ ग्राफ) के बीच सभी दिशाओं में यात्रा करनी होती है।

उस समय जब मैंने उस प्रोजेक्ट को दूसरे के लिए छोड़ दिया था, हर जगह गतिरोध के मुद्दे थे। मैंने सुना है कि 2-3 महीने बाद, कई अन्य डेवलपर्स ने गतिरोध के सभी मुद्दों को ठीक करने में कामयाब रहे, इस बिंदु पर कि इसे ग्राहकों को भेजा जा सकता है। मैं कभी भी यह पता लगाने में कामयाब नहीं हो पाया कि मेरे पास जो ज्ञान की कमी है।

परियोजना के बारे में कुछ: संदेश आईडी की संख्या (पूर्णांक मान जो किसी घटना के अर्थ का वर्णन करता है जिसे किसी अन्य वस्तु की संदेश कतार में भेजा जा सकता है, चाहे कोई भी सूत्र हो) कई हजारों में चलता है। अनोखे तार (उपयोगकर्ता संदेश) भी लगभग एक हजार में चलते हैं।

जोड़ा गया

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

साथी योगदानकर्ता:

(योगदानकर्ताओं को वास्तविक / व्यक्तिगत उदाहरणों का उपयोग करने की आवश्यकता नहीं है। उपाख्यानों के उदाहरणों से सबक, अगर यह अपने आप में विश्वसनीय हो, तो भी स्वागत है।)



मुझे लगता है कि 'थ्रेड्स में सोचने में सक्षम' कुछ हद तक एक प्रतिभा है और कम कुछ ऐसा है जिसे सीखा जा सकता है, बेहतर शब्दों की कमी के लिए। मैं बहुत से डेवलपर्स को जानता हूं जो बहुत लंबे समय से समानांतर सिस्टम के साथ काम कर रहे हैं, लेकिन अगर डेटा को एक से अधिक दिशाओं में जाना है तो वे चोक हो जाते हैं।
dauphic

जवाबों:


13

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

इसे ठीक करने का एकमात्र तरीका यह है कि सभी थ्रेड हैंडलिंग को कोड के एक छोटे टुकड़े के रूप में कड़ाई से कोरल करें जो इसे ठीक हो जाता है और जो यह सुनिश्चित करने के बारे में बहुत रूढ़िवादी है कि ताले ठीक से आयोजित किए जाते हैं (और विश्व स्तर पर अधिग्रहण का लगातार क्रम भी) । ऐसा करने का सबसे आसान तरीका यह है कि मैसेजिंग को छोड़कर थ्रेड्स के बीच मेमोरी (या अन्य संसाधन) साझा न करें जो कि एसिंक्रोनस होना चाहिए; यह आपको एक और शैली में सब कुछ लिखने देता है जो थ्रेड-विस्मृत है। (बोनस: एक क्लस्टर में कई मशीनों के लिए स्केलिंग करना बहुत आसान है।)


+1 के लिए "संदेश (या अन्य संसाधनों) को साझा न करने के लिए थ्रेड्स के बीच संदेश को छोड़कर जो अतुल्यकालिक होना चाहिए;"
नेमेनजा ट्रिफ़ुनोविक

1
केवल तरीका है? अपरिवर्तनीय डेटा प्रकारों के बारे में क्या?
Aaronaught

is that in a multithreaded program the scheduler is a sneaky swine that hates you.- नहीं, ऐसा नहीं है, यह वही करता है जो आपने इसे करने के लिए कहा था :)
मैटनज़

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

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

6

यहां कुछ बुनियादी सबक दिए गए हैं, जिनके बारे में मैं अभी सोच सकता हूं (विफल परियोजनाओं से नहीं, बल्कि वास्तविक परियोजनाओं पर देखे गए वास्तविक मुद्दों से):

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

6
  • आपके संपूर्ण GUI प्रोजेक्ट को केवल मुख्य थ्रेड से बुलाया जाना चाहिए । असल में, आपको अपने GUI में एक (.net) "इनवोक" नहीं डालना चाहिए। मल्टीथ्रेडिंग को अलग-अलग परियोजनाओं में अटक जाना चाहिए जो धीमे डेटा-एक्सेस को संभालते हैं।

हमें एक हिस्सा विरासत में मिला है जहां GUI परियोजना एक दर्जन धागे का उपयोग कर रही है। यह समस्याओं के अलावा कुछ नहीं दे रहा है। डेडलॉक, रेसिंग मुद्दे, क्रॉस थ्रेड GUI कॉल ...


क्या "प्रोजेक्ट" का अर्थ "असेंबली" है? मैं यह नहीं देखता कि असेंबली के बीच कक्षाओं के वितरण से कैसे समस्याएं फैलेंगी।
नीकी

मेरी परियोजना में यह वास्तव में एक विधानसभा है। लेकिन मुख्य बिंदु यह है कि उन फ़ोल्डरों में सभी कोड को मुख्य धागे से बुलाया जाना है, कोई अपवाद नहीं है।
कैर्रा

मुझे नहीं लगता कि यह नियम आम तौर पर लागू होता है। हां, आपको कभी भी किसी दूसरे धागे से GUI कोड को कॉल नहीं करना चाहिए। लेकिन आप कक्षाओं को फ़ोल्डर्स / प्रोजेक्ट / असेंबली में कैसे वितरित करते हैं यह एक स्वतंत्र निर्णय है।
निकाइ

1

जावा 5 और बाद में एक्जिक्यूटर्स हैं, जिनका उद्देश्य बहु-थ्रेडिंग कांटे-जॉइन शैली कार्यक्रमों को संभालने के लिए जीवन को आसान बनाना है।

उन का उपयोग करें, यह बहुत दर्द को दूर करेगा।

(और, हाँ, यह मैंने एक परियोजना से सीखा है :))


1
इस उत्तर को अन्य भाषाओं में लागू करने के लिए - जब भी संभव हो, उस भाषा द्वारा प्रदत्त उच्च गुणवत्ता वाले समानांतर प्रसंस्करण ढाँचों का उपयोग करें। (हालांकि, केवल समय बताएगा कि एक रूपरेखा वास्तव में महान और अत्यधिक प्रयोग करने योग्य है।)
rwong

1

मेरे पास हार्ड रीयलटाइम एम्बेडेड सिस्टम में एक पृष्ठभूमि है। आप मल्टीथ्रेडिंग के कारण होने वाली समस्याओं की अनुपस्थिति के लिए परीक्षण नहीं कर सकते। (आप कभी-कभी उपस्थिति की पुष्टि कर सकते हैं)। कोड को काफी सही साबित करना होगा। किसी भी और सभी थ्रेड इंटरैक्शन के आसपास सबसे अच्छा अभ्यास।

  • # 1 नियम: KISS - अगर स्पिन एक नहीं है एक धागा जरूरत नहीं है,। जितना हो सके सीरियल करें।
  • # 2 नियम: # 1 को मत तोड़ो।
  • # 3 यदि आप समीक्षा के माध्यम से यह साबित नहीं कर सकते कि यह सही है, तो यह नहीं।

नियम 1 के लिए 1। मैं एक ऐसी परियोजना पर काम कर रहा था जो शुरू में एक और धागा पूरा होने तक ब्लॉक करने जा रही थी - अनिवार्य रूप से एक विधि कॉल! सौभाग्य से, हमने उस दृष्टिकोण के खिलाफ फैसला किया।
माइकल के

# 3 FTW। लॉक टाइमिंग आरेख या जो कुछ भी आप यह साबित करने के लिए उपयोग करते हैं कि वह महीनों से अच्छा है यह सोचकर कि यह कभी-कभी क्यों गिर जाता है, के साथ संघर्ष करते हुए घंटे बिताने के लिए बेहतर है।

1

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

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

दूसरे शब्दों में, थ्रेडिंग मुद्दों से बचने की कोशिश में ओवरकिल के कारण कुछ सबसे खराब थ्रेडिंग मुद्दे हैं।


0

फिर से करने की कोशिश करें।

कम से कम मेरे लिए, क्या अंतर पैदा किया गया अभ्यास था। मल्टी थ्रेडेड और डिस्ट्रिब्यूटेड काम करने के बाद कई बार आप इसे लटका लेते हैं।

मुझे लगता है कि डिबगिंग वास्तव में क्या मुश्किल है। मैं वी.एस. का उपयोग करके बहु थ्रेडेड कोड को डीबग कर सकता हूं लेकिन अगर मुझे gdb का उपयोग करना है तो मैं वास्तव में पूरी तरह से नुकसान में हूं। मेरी गलती, शायद।

एक और चीज जो अधिक सीख रही है वह है लॉक फ्री डेटा स्ट्रक्चर्स।

मुझे लगता है कि यदि आप रूपरेखा निर्दिष्ट करते हैं तो यह प्रश्न वास्तव में बेहतर हो सकता है। .NET थ्रेड पूल और बैकग्राउंड वर्कर्स वास्तव में एक उदाहरण के लिए, क्वाथ्रेड से अलग हैं। वहाँ हमेशा कुछ मंच विशिष्ट gotchas है।


मैं किसी भी ढांचे से कहानियों को सुनने में दिलचस्पी रखता हूं, क्योंकि मेरा मानना ​​है कि प्रत्येक फ्रेमवर्क से सीखने वाली चीजें हैं, विशेष रूप से वे जो मुझे उजागर नहीं हुए हैं।
रवांग

1
डीबगर्स एक बहु-थ्रेड वातावरण में बड़े पैमाने पर बेकार हैं।
पेमदास

मेरे पास पहले से ही बहु-थ्रेडेड निष्पादन ट्रैसर हैं जो मुझे बताते हैं कि समस्या क्या है, लेकिन मुझे इसे हल करने में मदद नहीं करेगा। मेरी समस्या का दोष यह है कि "वर्तमान डिज़ाइन के अनुसार, मैं इस तरीके (अनुक्रम) में संदेश X को ऑब्जेक्ट Y से पास नहीं कर सकता; इसे एक विशाल कतार में जोड़ा जाना है और इसे अंततः संसाधित किया जाएगा; लेकिन इस वजह से , उपयोगकर्ता को सही समय पर प्रकट होने वाले संदेशों के लिए कोई रास्ता नहीं है - यह हमेशा अनिरोस्टिक तरीके से होगा और उपयोगकर्ता को बहुत भ्रमित करेगा , आपको उन स्थानों पर प्रगति बार, बटन रद्द करने या त्रुटि संदेश जोड़ने की आवश्यकता हो सकती है, जिन्हें नहीं करना चाहिए। ' टी उन है । "
rwong

0

मैंने सीखा है कि निचले स्तर के मॉड्यूल से उच्च स्तर के मॉड्यूल में कॉलबैक एक बहुत बड़ी बुराई है क्योंकि वे एक विपरीत क्रम में ताले प्राप्त करने का कारण बनते हैं।


कॉलबैक बुराई नहीं है ... थ्रेड ब्रेक के अलावा वे जो कुछ भी करते हैं वह शायद बुराई की जड़ है। मुझे किसी भी कॉलबैक पर अत्यधिक संदेह होगा जो संदेश कतार में सिर्फ टोकन नहीं भेजता है।
पेमदास

एक अनुकूलन समस्या को हल करना (जैसे कि f (x) को कम करना) अक्सर ऑप्टिमाइज़ेशन प्रक्रिया के लिए फ़ंक्शन f (x) को पॉइंटर प्रदान करके कार्यान्वित किया जाता है, जो न्यूनतम खोजते समय "इसे वापस कॉल करता है"। बिना कॉलबैक के आप इसे कैसे करेंगे?
मात्रा_देव

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

@ मिनी: यदि कॉलबैक के दौरान एक लॉक रखा जाना चाहिए, तो या तो बाकी एपीआई को फिर से तैयार (कठोर) होने की जरूरत है या यह तथ्य कि आप लॉक को पकड़ रहे हैं, एपीआई का एक दस्तावेज वाला हिस्सा होना चाहिए ( दुर्भाग्यपूर्ण है, लेकिन कभी-कभी आप सभी कर सकते हैं)।
डोनल फेलो

@ डॉनल फेलो: यदि कॉलबैक के दौरान लॉक होना चाहिए, तो मैं कहूंगा कि आपके पास एक डिज़ाइन दोष है। अगर वास्तव में कोई और रास्ता नहीं है, तो हाँ, हर तरह से इसे दस्तावेज़ करें! जैसे आप दस्तावेज़ करेंगे यदि कॉलबैक को बैकग्राउंड थ्रेड में कॉल किया जाएगा। वह इंटरफ़ेस का हिस्सा है।
निकेई
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.