मेरी नई टीम, जिसे मैं प्रबंधित करता हूं, हमारे कोड का अधिकांश प्लेटफ़ॉर्म, टीसीपी सॉकेट और http नेटवर्किंग कोड है। सभी सी ++। इसका अधिकांश हिस्सा अन्य डेवलपर्स से उत्पन्न हुआ, जिन्होंने टीम को छोड़ दिया है। टीम के वर्तमान डेवलपर बहुत स्मार्ट हैं, लेकिन अनुभव के मामले में ज्यादातर जूनियर हैं।
हमारी सबसे बड़ी समस्या: बहु-थ्रेडेड कंसीलर कीड़े। हमारे अधिकांश कक्षा पुस्तकालयों को कुछ थ्रेड पूल कक्षाओं के उपयोग से अतुल्यकालिक लिखा जाता है। कक्षा पुस्तकालयों पर विधियाँ अक्सर एक थ्रेड से थ्रेड पूल पर लंबे समय तक चलने वाली टाँके लगाती हैं और फिर उस कक्षा के कॉलबैक तरीकों को एक अलग धागे पर लागू किया जाता है। नतीजतन, हमारे पास बहुत सारे केस केस बग हैं जिनमें गलत थ्रेडिंग धारणाएं हैं। इसके परिणामस्वरूप सूक्ष्म कीड़े होते हैं जो महत्वपूर्ण अनुभागों से परे जाते हैं और संगामिति मुद्दों से बचाव के लिए लॉक होते हैं।
इन समस्याओं को और भी कठिन बना देता है जिसे ठीक करने का प्रयास अक्सर गलत होता है। कुछ गलतियाँ जो मैंने टीम के प्रयास में देखी हैं (या विरासत कोड के भीतर) निम्नलिखित में से कुछ शामिल हैं:
सामान्य गलती # 1 - केवल साझा किए गए डेटा के चारों ओर लॉक लगाकर समवर्ती समस्या को हल करना, लेकिन जब कोई अपेक्षित क्रम में विधियों को नहीं बुलाया जाता है तो क्या होता है, इसके बारे में भूल जाना। यहाँ एक बहुत ही सरल उदाहरण दिया गया है:
void Foo::OnHttpRequestComplete(statuscode status)
{
m_pBar->DoSomethingImportant(status);
}
void Foo::Shutdown()
{
m_pBar->Cleanup();
delete m_pBar;
m_pBar=nullptr;
}
तो अब हमारे पास एक बग है जिसमें शटडाउन को बुलाया जा सकता है जबकि OnHttpNetworkRequestComplete चल रहा है। एक परीक्षक बग को ढूंढता है, क्रैश डंप को पकड़ता है, और बग को एक डेवलपर को सौंपता है। वह इस तरह बग को ठीक करता है।
void Foo::OnHttpRequestComplete(statuscode status)
{
AutoLock lock(m_cs);
m_pBar->DoSomethingImportant(status);
}
void Foo::Shutdown()
{
AutoLock lock(m_cs);
m_pBar->Cleanup();
delete m_pBar;
m_pBar=nullptr;
}
उपरोक्त फिक्स तब तक अच्छा लगता है जब तक आपको एहसास न हो कि एक और भी सूक्ष्म किनारे का मामला है। यदि OnHttpRequestComplete को वापस बुलाया जाता है, तो शटडाउन को कॉल करने पर क्या होता है ? मेरी टीम के पास वास्तविक दुनिया के उदाहरण और भी अधिक जटिल हैं, और कोड की समीक्षा प्रक्रिया के दौरान किनारे के मामले और भी कठिन हैं।
कॉमन मिस्टेक # 2 - लॉक से बाहर निकलने के द्वारा डेडलॉक मुद्दों को ठीक करना, दूसरे थ्रेड के समाप्त होने की प्रतीक्षा करें, फिर लॉक को फिर से दर्ज करें - लेकिन इस मामले को हैंडल किए बिना कि ऑब्जेक्ट दूसरे थ्रेड द्वारा अपडेट हो गया है!
कॉमन मिस्टेक # 3 - भले ही ऑब्जेक्ट को रेफरेंस काउंट किया गया हो, शटडाउन सीक्वेंस "पॉइंटर" है। लेकिन उस थ्रेड के लिए इंतजार करना भूल जाता है जो अभी भी जारी करने के लिए चल रहा है। जैसे, घटकों को सफाई से बंद कर दिया जाता है, फिर किसी वस्तु पर स्पुरियस या लेट कॉलबैक किसी भी अधिक कॉल की उम्मीद नहीं करता है।
अन्य किनारे मामले हैं, लेकिन नीचे की रेखा यह है:
मल्टीथ्रेडेड प्रोग्रामिंग सिर्फ सादा कठिन है, स्मार्ट लोगों के लिए भी।
जैसा कि मैं इन गलतियों को पकड़ता हूं, मैं प्रत्येक डेवलपर के साथ त्रुटियों पर चर्चा करने में समय बिताता हूं ताकि एक अधिक उपयुक्त निर्धारण विकसित किया जा सके। लेकिन मुझे संदेह है कि वे अक्सर इस बात पर भ्रमित होते हैं कि प्रत्येक मुद्दे को विरासत कोड की भारी मात्रा में हल करने के कारण "राइट" फिक्स में स्पर्श शामिल होगा।
हम जल्द ही शिपिंग होने जा रहे हैं, और मुझे यकीन है कि हम जो पैच लागू कर रहे हैं वह आगामी रिलीज के लिए होगा। बाद में, हमें कोड बेस और रिफ्लेक्टर को सुधारने के लिए कुछ समय चाहिए। हमारे पास सब कुछ फिर से लिखने का समय नहीं होगा। और कोड के बहुमत यह सब बुरा नहीं है। लेकिन मैं रिफ्लेक्टर कोड को देख रहा हूं ताकि थ्रेडिंग के मुद्दों को पूरी तरह से टाला जा सके।
एक दृष्टिकोण जिस पर मैं विचार कर रहा हूं वह यह है। प्रत्येक महत्वपूर्ण प्लेटफ़ॉर्म सुविधा के लिए, एक समर्पित सिंगल थ्रेड है, जहाँ सभी ईवेंट और नेटवर्क कॉलबैक पर मार्शेल्ड हो जाते हैं। एक संदेश लूप के उपयोग के साथ विंडोज में सूत्रण COM अपार्टमेंट के समान है। लंबे समय तक अवरुद्ध संचालन अभी भी एक कार्य पूल थ्रेड के लिए भेजा जा सकता है, लेकिन घटक के धागे पर पूरा कॉलबैक लागू किया जाता है। घटक संभवतः उसी धागे को साझा भी कर सकते हैं। फिर धागे के अंदर चलने वाले सभी कक्षा पुस्तकालयों को एक एकल पिरोया दुनिया की धारणा के तहत लिखा जा सकता है।
इससे पहले कि मैं उस रास्ते से नीचे जाऊं, मुझे बहुत दिलचस्पी है अगर मल्टीथ्रेडेड मुद्दों से निपटने के लिए अन्य मानक तकनीक या डिज़ाइन पैटर्न हैं। और मुझे जोर देना है - एक किताब से परे कुछ ऐसा जो म्यूटेक्स और सेमाफोर की मूल बातों का वर्णन करता है। तुम क्या सोचते हो?
मैं भी किसी भी अन्य दृष्टिकोण में दिलचस्पी ले रहा हूँ एक refactoring प्रक्रिया की ओर ले जाने के लिए। निम्नलिखित में से कोई भी शामिल है:
धागे के चारों ओर डिजाइन पैटर्न पर साहित्य या कागजात। म्यूटेक्स और सेमाफोरस के परिचय से परे कुछ। हमें बड़े पैमाने पर समानता की आवश्यकता नहीं है, बस किसी ऑब्जेक्ट मॉडल को डिजाइन करने के तरीके ताकि अन्य धागे से अतुल्यकालिक घटनाओं को सही ढंग से संभाल सकें ।
विभिन्न घटकों के थ्रेडिंग को आरेखित करने के तरीके, ताकि इसके लिए समाधानों का अध्ययन और विकास करना आसान हो। (यानी, वस्तुओं और वर्गों के बीच चर्चा के लिए एक यूएमएल समकक्ष)
मल्टीट्रेड कोड वाले मुद्दों पर अपनी विकास टीम को शिक्षित करना।
तुम क्या करोगे?