प्रॉम्प्ट और संदर्भ
एक बैकग्राउंड वर्कर लागू करें जो एक क्यू (queue) को कंस्यूम करता है। शटडाउन या अपस्ट्रीम कैंसिलेशन के दौरान, मेन थ्रेड स्टॉप का अनुरोध करता है; शुरू हो चुका टास्क पूरा होता है, जबकि बिना शुरू हुआ कार्य या तो रख लिया जाता है या स्पष्ट रूप से डिस्कार्ड कर दिया जाता है। बिज़ी वेटिंग, डेटा रेस, डिस्ट्रक्शन के दौरान जीवित थ्रेड, और ऐसे कंडीशन-वेरिएबल वेट से बचें जो कभी वापस नहीं लौटता।
C++20 std::jthread डिस्ट्रक्शन के दौरान स्टॉप का अनुरोध करता है और जॉइन करता है, और एंट्री फ़ंक्शन में एक std::stop_token इंजेक्ट कर सकता है। एक स्टॉप रिक्वेस्ट कोऑपरेटिव होती है; यह किसी भी कोड को जबरन समाप्त नहीं कर सकती। वर्कर को पोल करना होगा या स्टॉप-अवेयर प्रिमिटिव के साथ प्रतीक्षा करनी होगी।
इंटरव्यूअर क्या मूल्यांकन करता है
- आप समझते हैं कि स्टॉप टोकन शेयर्ड स्टेट पर एक अनुरोध है, न कि एसिंक्रोनस थ्रेड किलिंग।
- आप
std::jthreadऑटोमैटिक जॉइनिंग का उपयोग करते हैं और जब तक थ्रेड ऑब्जेक्ट मेंबर्स को एक्सेस करता है, तब तक उन्हें जीवित रखते हैं। - आप ब्लॉकिंग वेट को इंटरप्टिबल बनाते हैं, उदाहरण के लिए
condition_variable_anyस्टॉप-टोकन ओवरलोड के साथ। - आप क्यू, स्टॉप स्टेट, टास्क एक्सेप्शन और क्लीनअप लाइफटाइम को सुरक्षित रखते हैं।
- आप खाली-क्यू स्टॉप, स्टॉप रेस, टास्क एक्सेप्शन, बार-बार
request_stop, और डिस्ट्रक्शन ऑर्डर का परीक्षण करते हैं।
पहले स्पष्ट करने योग्य प्रश्न
- क्या चल रहा टास्क समाप्त हो सकता है, और क्या यह इडेम्पोटेंट है या बाहरी साइड-इफ़ेक्ट वाला है?
- जब क्यू बंद हो जाती है, तो क्या अनकंस्यूम्ड टास्क छोड़ दिए जाते हैं, ट्रांसफर कर दिए जाते हैं, या किसी अन्य वर्कर द्वारा ड्रेन किए जाते हैं?
- क्या वेट इंटरप्टिबल है, या यह किसी थर्ड-पार्टी I/O कॉल पर निर्भर करता है जिसे कैंसिल नहीं किया जा सकता?
- क्या एक्सेप्शन रिकॉर्ड किए जाते हैं, जॉइन करने वाले थ्रेड में प्रोपेगेट किए जाते हैं, या सेवा को रोकने के लिए उपयोग किए जाते हैं?
- क्या कई थ्रेड स्टॉप, डिस्ट्रक्शन, या रीस्टार्ट को कॉल कर सकते हैं?
30-सेकंड का उत्तर
“वर्कर का स्वामित्व std::jthread के पास रखें और इसके एंट्री फ़ंक्शन में एक std::stop_token स्वीकार करें। क्यू को म्यूटेक्स से सुरक्षित रखें और स्टॉप-अवेयर condition_variable_any का उपयोग करके प्रतीक्षा करें; जागने के बाद स्टॉप, क्यू क्लोजर, और टास्क स्टेट की जाँच करें। एक डीक्यू किया गया टास्क निर्धारित कैंसिलेशन पॉइंट्स तक चलता है और क्लीनअप करता है। डिस्ट्रक्शन स्टॉप का अनुरोध करता है और जॉइन करता है; यह कभी डिटैच नहीं करता। शेयर्ड स्टेट थ्रेड से अधिक समय तक जीवित रहती है। टेस्ट स्टॉप रेस और एक्सेप्शन को कवर करते हैं।”
चरण-दर-चरण समाधान
चरण 1: कैंसिलेशन कॉन्ट्रैक्ट को परिभाषित करें
“स्टॉप रिक्वेस्टेड” को “टास्क कंप्लीटेड” से अलग करें। एक स्टॉप रिक्वेस्ट नए काम को रोकती है; शुरू किया गया टास्क समाप्त होता है या सुरक्षित बिंदुओं पर कैंसिलेशन परिणाम लौटाता है। आर्बिट्रेरी थर्ड-पार्टी कॉल के तत्काल कैंसिलेशन का वादा न करें।
चरण 2: ओनरशिप को परिभाषित करें
क्यू, म्यूटेक्स, कंडीशन वेरिएबल और std::jthread के मालिक ऑब्जेक्ट को थ्रेड की तुलना में अधिक समय तक जीवित रखें। डिस्ट्रक्शन स्टॉप का अनुरोध करता है और मेंबर्स को रिलीज़ करने से पहले प्रतीक्षा करता है। कभी भी किसी डेड स्कोप के संदर्भ को कैप्चर न करें या किसी लेट कॉलबैक को रॉ this पब्लिश न करें।
चरण 3: वेट को कैंसिलेबल बनाएं
condition_variable_any स्टॉप-टोकन वेट का उपयोग करें या एक stop_callback रजिस्टर करें जो notify_all को कॉल करता है। प्रेडिकेट क्यू नॉन-एम्प्टी, क्लोज्ड स्टेट, और stop_requested() की जाँच करता है; वेक-अप लॉक को फिर से हासिल करते हैं और स्टेट की पुन: जाँच करते हैं।
std::jthread worker([this](std::stop_token st) {
for (;;) {
Task task;
{
std::unique_lock lock(mu_);
cv_.wait(lock, st, [this, &st] {
return closed_ || !queue_.empty() || st.stop_requested();
});
if (st.stop_requested() || (closed_ && queue_.empty())) return;
task = std::move(queue_.front());
queue_.pop_front();
}
run(task, st);
}
});चरण 4: टास्क स्टॉप पॉइंट्स और एक्सेप्शन को संभालें
टास्क चरणों के बीच टोकन की जाँच करें और आंशिक राइट्स से बचने के लिए साइड-इफ़ेक्ट सीमाओं को परिभाषित करें। थ्रेड बाउंड्री पर एक्सेप्शन को कैच करें, टास्क ID और एरर रिकॉर्ड करें, और तय करें कि वर्कर को जारी रखना है या रोकना है। किसी भी एक्सेप्शन को एंट्री फ़ंक्शन से बाहर न निकलने दें।
चरण 5: सही क्रम में बंद करें
नए टास्क को अस्वीकार करें, क्यू को क्लोज्ड मार्क करें, वेटर्स को सूचित करें, स्टॉप का अनुरोध करें, जॉइन करें, और उसके बाद ही संसाधनों को रिलीज़ करें। ड्रेन टाइमआउट और बचे हुए टास्क हैंडलिंग को परिभाषित करें। बार-बार request_stop() सुरक्षित होना चाहिए और साइड इफेक्ट्स को दोहराना नहीं चाहिए।
चरण 6: रेस का परीक्षण करें और व्यवहार का निरीक्षण करें
खाली-क्यू स्टॉप, डीक्यू के दौरान स्टॉप, एक साथ स्टॉप कॉल, डिस्ट्रक्शन के दौरान नोटिफिकेशन, टास्क एक्सेप्शन, ब्लॉकिंग-I/O टाइमआउट, और बार-बार क्लोज़ का परीक्षण करें। स्टॉप लेटेंसी, पूरे/रद्द किए गए टास्क, बची हुई क्यू, एक्सेप्शन, और जॉइन टाइम रिकॉर्ड करें; रेस के लिए ThreadSanitizer चलाएं।
एक मजबूत नमूना उत्तर
“मैं वर्कर के लाइफटाइम को jthread के साथ मैनेज करता हूँ और stoptoken स्वीकार करता हूँ। एक म्यूटेक्स क्यू स्टेट की सुरक्षा करता है; एक स्टॉप-अवेयर conditionvariable_any क्लोज्ड, नॉन-एम्प्टी, और स्टॉप रिक्वेस्ट की जाँच करता है, ताकि स्टॉप वेट को जगा दे। डीक्यू के बाद मैं लॉक रिलीज़ कर देता हूँ। टास्क सुरक्षित बिंदुओं पर टोकन की जाँच करता है और ट्रांजैक्शन क्लीनअप पूरा करता है। एंट्री एक्सेप्शन को कैच और रिकॉर्ड करती है।”
“शटडाउन नए काम को अस्वीकार करता है, क्लोज्ड सेट करता है, सूचित करता है, स्टॉप का अनुरोध करता है, और जॉइन करता है। यह कभी भी डिटैच नहीं करता है और न ही क्यू और लॉगर को जल्दी रिलीज़ करता है। टेस्ट एक खाली क्यू, डीक्यू रेस, एक्सेप्शन, बार-बार स्टॉप, लॉन्ग-टास्क टाइमआउट, और ThreadSanitizer को कवर करते हैं।”
सामान्य गलतियाँ
- stop_token को जबरन समाप्त करने जैसा मानना → संसाधन और ट्रांजैक्शन टूट जाते हैं → कोऑपरेटिव पॉइंट्स परिभाषित करें।
std::threadको जॉइन करना भूल जाना → टर्मिनेशन या एक डैंगलिंग थ्रेड → jthread या स्पष्ट लाइफटाइम ओनरशिप का उपयोग करें।- एक नोटिफिकेशन की प्रतीक्षा करना → छूटा हुआ नोटिफिकेशन हमेशा के लिए सो जाता है → प्रेडिकेट लूप का उपयोग करें और स्टॉप पर जागें।
- लॉक रखते हुए टास्क चलाना → प्रोड्यूसर्स और शटडाउन ब्लॉक हो जाते हैं → डीक्यू के बाद रिलीज़ करें।
- एक्सेप्शन को एंट्री से बाहर निकलने देना → प्रोसेस समाप्त हो जाता है → थ्रेड बाउंड्री पर कैच करें।
- थ्रेड को रोकने से पहले मेंबर्स को रिलीज़ करना → यूज़-आफ्टर-फ्री → स्टॉप करें, जॉइन करें, फिर स्टेट रिलीज़ करें।
फॉलो-अप प्रश्न और उत्तर
एक jthread डिस्ट्रक्टर क्या करता है?
यदि यह जॉइन करने योग्य (joinable) है, तो डिस्ट्रक्शन स्टॉप का अनुरोध करता है और जॉइन करता है; यह टास्क को जबरन समाप्त नहीं करता है। टास्क को प्रतिक्रिया देनी होगी, इसलिए जॉइन एक सुरक्षित बिंदु की प्रतीक्षा कर सकता है।
क्या कोई स्टॉप रिक्वेस्ट कंडीशन वेरिएबल को जगा सकती है?
condition_variable_any का स्टॉप-टोकन ओवरलोड तब रिटर्न करता है जब स्टॉप का अनुरोध किया जाता है। एक कस्टम वेट को सूचित करने के लिए एक स्टॉप कॉलबैक और एक प्रेडिकेट की आवश्यकता होती है जो स्थिति की फिर से जाँच करता है।
क्या इन-फ़्लाइट डेटाबेस राइट को तुरंत रद्द किया जा सकता है?
ऐसा मानकर न चलें। रोलबैक करने योग्य या इडेम्पोटेंट चरणों, ड्राइवर के टाइमआउट/कैंसिलेशन सपोर्ट, और कमिट बाउंड्री पर स्टॉप चेक का उपयोग करें।
आप स्टॉप रेस से कैसे बचते हैं?
क्लोज्ड, क्यू, और स्टॉप को एक ही लाइफसाइकिल प्रोटोकॉल मानें। लॉक के तहत ट्रांज़िशन करें और स्टेट बदलने के बाद सूचित करें; उस विंडो पर स्ट्रेस टेस्ट करें जहाँ स्टॉप और डीक्यू एक साथ होते हैं और ThreadSanitizer चलाएं।