मैं एक कस्टम समवर्ती dispatch_queue पर dispatch_sync का उपयोग करते समय अपने ऐप में एक अस्थायी डेडलॉक देख रहा हूं। मैं समवर्ती पढ़ने के उपयोग का समर्थन करने के लिए Mike Ash's blog में वर्णित विधि के समान कुछ उपयोग कर रहा हूं लेकिन एनएसएमयूटेबल डिक्शनरी पर थ्रेडसेफ उत्परिवर्तन जो वर्तमान में सक्रिय नेटवर्क आरपीसी अनुरोधों के कैश के रूप में कार्य करता है। मेरी परियोजना एआरसी का उपयोग करती है।कस्टम समवर्ती कतार डेडलॉकिंग पर dispatch_sync क्यों है
dispatch_queue_t activeRequestsQueue = dispatch_queue_create("my.queue.name",
DISPATCH_QUEUE_CONCURRENT);
और साथ
NSMutableDictionary *activeRequests = [[NSMutable dictionary alloc] init];
मैं इस तरह कतार से तत्वों को पढ़ने के परिवर्तनशील शब्दकोश:
- (id)activeRequestForRpc: (RpcRequest *)rpc
{
assert(![NSThread isMainThread]);
NSString * key = [rpc getKey];
__block id obj = nil;
dispatch_sync(activeRequestsQueue, ^{
obj = [activeRequests objectForKey: key];
});
return obj;
}
मैं जोड़ सकते हैं और RPC शुरू हटाने
मैं के साथ कतार बना कैश से
- (void)addActiveRequest: (RpcRequest *)rpc
{
NSString * key = [rpc getKey];
dispatch_barrier_async(activeRequestsQueue, ^{
[activeRequests setObject: rpc forKey: key];
});
}
- (void)removeActiveRequest: (RpcRequest *)rpc
{
NSString * key = [rpc getKey];
dispatch_barrier_async(activeRequestsQueue, ^{
[activeRequests removeObjectForKey:key];
});
}
मैं सक्रिय RequestForRpc पर कॉल में डेडलॉक देख रहा हूं, जब मैं कई बार नेटवर्क अनुरोध करता हूं जो मुझे विश्वास दिलाता है कि बाधा ब्लॉक (जोड़ें या निकालें) में से एक निष्पादन को पूरा नहीं कर रहा है। मैं हमेशा पृष्ठभूमि थ्रेड से activeRequestForRpc को कॉल करता हूं, और ऐप यूआई फ्रीज नहीं करता है इसलिए मुझे नहीं लगता कि इसे मुख्य थ्रेड को अवरुद्ध करना है, लेकिन मैंने मामले में केवल कथन कथन जोड़ा है। इस डेडलॉक कैसे हो रहा है इस पर कोई विचार?
अद्यतन: कोड कहा कि बुला रहा है इन तरीकों
मैं नेटवर्क अनुरोध करने के लिए AFNetworking उपयोग कर रहा हूँ और मैं एक NSOperationQueue है कि मैं 'के एक ओर कैश और शायद नेटवर्क से संसाधन लाने' का समय निर्धारण कर रहा हूँ है तर्क। मैं उस चेक को चेककैच और FetchFromNetworkOp पर कॉल करूंगा। उस ओप के अंदर मैं आरपीसी अनुरोध करने के लिए AFHTTPClient के अपने कस्टम उप-वर्ग में कॉल करता हूं।
// this is called from inside an NSOperation executing on an NSOperationQueue.
- (void) enqueueOperation: (MY_AFHTTPRequestOperation *) op {
NSError *error = nil;
if ([self activeRequestForRpc:op.netRequest.rpcRequest]) {
error = [NSError errorWithDomain:kHttpRpcErrorDomain code:HttpRpcErrorDuplicate userInfo:nil];
}
// set the error on the op and cancels it so dependent ops can continue.
[op setHttpRpcError:error];
// Maybe enqueue the op
if (!error) {
[self addActiveRequest:op.netRequest.rpcRequest];
[self enqueueHTTPRequestOperation:op];
}
}
MY_AFHTTRequestOperation AFHTTPClient उदाहरण से और दोनों सफलता और विफलता के पूरा होने के ब्लॉक मैं [self removeActiveRequest:netRequest.rpcRequest];
पहली कार्रवाई के रूप में बुलाने के अंदर बनाया गया है। इन ब्लॉकों को मुख्य थ्रेड पर एएफनेटवर्किंग द्वारा डिफ़ॉल्ट व्यवहार के रूप में निष्पादित किया जाता है।
मैंने देखा है कि डेडलॉक होता है जहां अंतिम अवरोध ब्लॉक जो कतार पर लॉक धारण करना चाहिए, वह ब्लॉक ब्लॉक और निकालना ब्लॉक दोनों है।
क्या यह संभव है कि सिस्टम मेरे NSOperationQueue में CheckCacheAndFetchFromNetworkOp Ops का समर्थन करने के लिए अधिक धागे पैदा करता है, सक्रिय RequestsQueue निर्धारित होने के लिए बहुत कम प्राथमिकता होगी? यह डेडलॉक का कारण बन सकता है अगर सभी थ्रेड्स को चेककैच एंडफैचफ्रॉमनेटवर्क द्वारा अवरुद्ध किया गया था, सक्रिय सक्रियता शब्दकोश से कोशिश करने और पढ़ने के लिए अवरुद्ध किया गया था, और activeRequestsQueue एक ऐड/निकालें बाधा ब्लॉक पर अवरुद्ध कर रहा था जो निष्पादित नहीं कर सका।
अद्यतन
की स्थापना NSOperationQueue 1 (या वास्तव में कुछ डिफ़ॉल्ट NSOperationQueueDefaultMaxConcurrentOperationCount से उचित अन्य) की maxConcurrentOperation गिनती के लिए द्वारा समस्या का समाधान किया।
असल में मैंने जो सबक लिया है वह यह है कि आपके पास किसी भी अन्य प्रेषण_क्यूयू_टी या एनएसओपरेशनक्यूयू पर डिफ़ॉल्ट अधिकतम ऑपरेशन गिनती के साथ एनएसओपरेशनक्यूयू नहीं होना चाहिए क्योंकि यह संभावित रूप से उन अन्य कतारों से सभी धागे को हॉग कर सकता है।
यही हो रहा था।
कतार - NSOperationQueue डिफ़ॉल्ट NSDefaultMaxOperationCount पर सेट है जो सिस्टम को यह निर्धारित करने देता है कि कितने समवर्ती सेशन चलाने के लिए।
सेशन - कतार 1 पर चलता है और यह सुनिश्चित करने के बाद एपीनेटवर्किंग कतार पर नेटवर्क अनुरोध शेड्यूल करता है कि आरपीसी सक्रिय रिक्वेस्ट सेट में नहीं है।
सिस्टम यह निर्धारित करता है कि यह 10 समवर्ती धागे समर्थन कर सकते हैं (वास्तविकता में यह अधिक 80 की तरह था):
यहाँ प्रवाह है।
10 ऑप्स एक बार में निर्धारित हो जाते हैं। सिस्टम 10 ओप्स को इसके 10 धागे पर एक साथ चलने देता है। सभी 10 ऑप्स कॉल मेंActiveRequestForRPC है जो ActiveRequestQueue पर एक सिंक ब्लॉक शेड्यूल करता है और 10 थ्रेड को अवरुद्ध करता है। ActiveRequestQueue इसे पढ़ने के ब्लॉक को चलाने के लिए चाहता है, लेकिन इसमें कोई उपलब्ध थ्रेड नहीं है। इस बिंदु पर हमारे पास पहले से ही एक डेडलॉक है।
अधिकतर मुझे 9 ऑप्स (1-9) शेड्यूल की तरह कुछ दिखाई देगा, उनमें से एक, ओपी 1, 10 वीं थ्रेड पर एक्टिव रिवेस्टफोरआरपीसी चलाता है और एक एडएक्टिव रिवेस्ट बैरर ब्लॉक शेड्यूल करता है। फिर एक और सेशन 10 वें धागे पर निर्धारित होगा और op2-10 शेड्यूल करेगा और एकActiveRequestForRPC पर प्रतीक्षा करेगा। तब op1 के शेड्यूल किए गए एडआरपीसी ब्लॉक नहीं चलेंगे क्योंकि ओपन 10 ने आखिरी उपलब्ध थ्रेड लिया था, और अन्य सभी के पास एक्टिव रिवेस्टफॉरआरपीसी ब्लॉक अवरोध ब्लॉक को निष्पादित करने की प्रतीक्षा करेंगे। ओपी 1 बाद में अवरुद्ध हो जाएगा जब उसने एक अलग ऑपरेशन कतार पर कैश ऑपरेशन शेड्यूल करने का प्रयास किया था जो किसी भी थ्रेड तक पहुंच नहीं पा सका।
मैं यह सोचते गया था कि अवरुद्ध hasActiveRequestForRPC निष्पादित करने के लिए एक barrer खंड पर इंतज़ार कर रहे थे, लेकिन कुंजी activeRequestQueue किसी भी धागा उपलब्धता पर इंतजार कर रहा था।
क्या getKey (आपको इसे कुंजी कहा जाना चाहिए) विधि ऑब्जेक्ट को म्यूटेक्स से सुरक्षित रखती है? यदि ऐसा है तो समस्या यह है कि सक्रिय RequestForRpc में: mutex हमेशा लॉक होता है, और यह समाप्त नहीं होता है। –
नहीं, यह ऑब्जेक्ट को म्यूटेक्स से सुरक्षित नहीं करता है क्योंकि आरपीसी अपरिवर्तनीय हैं और मुझे नहीं लगता था कि इसकी आवश्यकता थी। यह वास्तव में एक ही कक्षा में एक और विधि को बुला रहा है जो आरपीसी से एक कुंजी उत्पन्न करता है, लेकिन मैंने सादगी के लिए इसे इस तरह लिखा है।कार्यान्वयन इस तरह दिखता है: विधि हस्ताक्षर {वापसी [NSSTringWithFormat, "स्ट्रिंग% @,% @ ..." rpc.someField ...]; } –
क्या यह संभव है कि इस विधि को एक ही कतार से बुलाया जा रहा है? सुनिश्चित करें कि 'dispatch_get_current_queue! = activeRequestQueue' –