गतिरोधों पर बहस करते समय आप क्या देखते हैं?


25

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

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

गतिरोधों पर बहस करते समय आप क्या देखते हैं?


मैं एसओ के बजाय यहां यह पूछ रहा हूं क्योंकि मैं गतिरोध डिबगिंग के बारे में अधिक सामान्य संकेत चाहता हूं, मेरी समस्या का कोई विशिष्ट जवाब नहीं।
माइकल के

रणनीतियाँ मैं के बारे में सोच सकते हैं प्रवेश कर रहे हैं (के रूप में कई अन्य लोगों ने बताया है), वास्तव में who's-इंतजार कर के लिए एक ताला आयोजित-दर-जिसे (देखें के गतिरोध ग्राफ की जांच stackoverflow.com/questions/3483094/... कुछ के लिए संकेत) और लॉक एनोटेशन (देखें clang.llvm.org/docs/ThreadSafetyAnalysis.html )। यहां तक ​​कि अगर यह आपका कोड नहीं है, तो आप लेखक को एनोटेशन जोड़ने के लिए समझाने की कोशिश कर सकते हैं - वे शायद कीड़े ढूंढेंगे और उन्हें (संभवतः आपके सहित) ठीक कर देंगे।
डॉन हैच

जवाबों:


23

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

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

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

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


4
+1 वाह, यह निराशावादी है ... हालांकि यह सच नहीं है। यह एक दिया है कि आप सभी बग नहीं पा सकते हैं। सुझाव के लिए धन्यवाद!
माइकल के

ब्रूस, "वास्तविक गतिरोध" का आपका सरलीकरण मेरे लिए आश्चर्य की बात है। मैंने सोचा कि दो थ्रेड्स के बीच एक गतिरोध है जब प्रत्येक लॉक का इंतजार कर रहा है जो दूसरे को पकड़ता है। आपकी परिभाषा में यह मामला भी शामिल है कि एक धागा, एक लॉक को पकड़े हुए, एक दूसरे लॉक को प्राप्त करने के लिए इंतजार करता है जो वर्तमान में एक अलग थ्रेड द्वारा आयोजित किया जाता है। यह मेरे लिए गतिरोध की तरह नहीं है; क्या यह??
डॉन हैच

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

+1 कोड को ध्यान से पढ़ने और अलगाव में सभी लॉक ऑपरेशन की जांच करने का सुझाव देता है। एक जटिल नोड को देखने के लिए बहुत आसान है एक एकल नोड की सावधानीपूर्वक जांच करें कि वह एक बार में पूरी चीज को देखने की कोशिश करता है। कोड को घूरकर और मेरे सिर में विभिन्न परिदृश्यों को चलाकर मैंने कितनी बार समस्या पाई है।
न्यूटोपियन

11
  1. जैसा कि दूसरों ने कहा है ... यदि आप लॉगिंग के लिए उपयोगी जानकारी प्राप्त कर सकते हैं तो कोशिश करें कि सबसे पहले यह सबसे आसान काम है।

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

  3. एक और चीज जो आप आजमा सकते हैं वह है अपने म्यूटेक्स / सेमाफोर सेवाओं के आसपास एक रैपर लाइब्रेरी का निर्माण करना। ट्रैक करें कि किस धागे में प्रत्येक म्यूटेक्स है और कौन से थ्रेड म्यूटेक्स पर प्रतीक्षा कर रहे हैं। एक मॉनिटर थ्रेड बनाएँ जो यह जाँचता है कि थ्रेड्स कितने लंबे समय से ब्लॉक हो रहे हैं। कुछ उचित अवधि पर ट्रिगर और उस राज्य की जानकारी को डंप करें जिसे आप ट्रैक कर रहे हैं।

कुछ बिंदु पर, सादे पुराने कोड निरीक्षण आवश्यक होने जा रहे हैं।


6

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

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

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


"अपवाद जारी करने से रोक सकता है" -> मुझे दया आती है कि जिन भाषाओं में स्कोप चर नहीं हैं: /
मैथ्यू एम।

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

3

कोड के भीतर दिलचस्प स्थानों पर मेरे सबसे अच्छे दोस्त प्रिंट / लॉग स्टेटमेंट रहे हैं। ये आमतौर पर मुझे बेहतर तरीके से समझने में मदद करते हैं कि ऐप के अंदर क्या चल रहा है, विभिन्न थ्रेड्स के बीच समय को बाधित किए बिना, जो बग को पुन: उत्पन्न करने से रोक सकता है।

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


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

3

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

यह कहते हुए कि, जैसे Péter Török ने कहा, लॉगिंग शायद ऐसा ही हो। जहां तक ​​मुझे पता है, डीबगर ने मल्टी-थ्रेडिंग वातावरण पर एक बुरा काम किया। यह पता लगाने की कोशिश करें कि ताला कहाँ है, संपूर्ण संसाधन प्राप्त करें और प्रतीक्षा किस स्थिति में हो।


नहीं, लॉगिंग आपका दुश्मन है - जब आप धीमी लॉगिंग डालते हैं, तो आप प्रोग्राम के व्यवहार को उस बिंदु पर बदल देते हैं, जहां प्रोग्राम को प्राप्त करना आसान है जो लॉगिंग सक्षम होने के साथ पूरी तरह से ठीक चलता है, लेकिन लॉगिंग बंद होने पर गतिरोध। इसकी एक ही तरह की समस्या आपको मल्टीकोर सीपीयू के बजाय एक एकल पर एक प्रोग्राम चलाने पर होती है।
gbjbaanb

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

0

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


0

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

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

और फिर एक पुरानी लेकिन अचूक रणनीति है: प्रत्येक स्तर पर एक "स्तर" असाइन करें, स्तर 0. पर शुरू। यदि आप एक स्तर 0 ताला लेते हैं तो आपको किसी अन्य ताले की अनुमति नहीं है। लेवल 1 लॉक लेने के बाद आप लेवल 0 लॉक ले सकते हैं। एक लेवल 10 लॉक लेने के बाद आप लेवल 9 या लोअर आदि ले सकते हैं।

यदि आपको ऐसा करना असंभव लगता है, तो आपको अपना कोड ठीक करना होगा क्योंकि आप गतिरोध में चले जाएंगे।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.