2012-12-19 23 views
12

मैं एक कस्टम समवर्ती 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 किसी भी धागा उपलब्धता पर इंतजार कर रहा था।

+0

क्या getKey (आपको इसे कुंजी कहा जाना चाहिए) विधि ऑब्जेक्ट को म्यूटेक्स से सुरक्षित रखती है? यदि ऐसा है तो समस्या यह है कि सक्रिय RequestForRpc में: mutex हमेशा लॉक होता है, और यह समाप्त नहीं होता है। –

+0

नहीं, यह ऑब्जेक्ट को म्यूटेक्स से सुरक्षित नहीं करता है क्योंकि आरपीसी अपरिवर्तनीय हैं और मुझे नहीं लगता था कि इसकी आवश्यकता थी। यह वास्तव में एक ही कक्षा में एक और विधि को बुला रहा है जो आरपीसी से एक कुंजी उत्पन्न करता है, लेकिन मैंने सादगी के लिए इसे इस तरह लिखा है।कार्यान्वयन इस तरह दिखता है: विधि हस्ताक्षर {वापसी [NSSTringWithFormat, "स्ट्रिंग% @,% @ ..." rpc.someField ...]; } –

+1

क्या यह संभव है कि इस विधि को एक ही कतार से बुलाया जा रहा है? सुनिश्चित करें कि 'dispatch_get_current_queue! = activeRequestQueue' –

उत्तर

3

संपादित करें: समस्या यह है कि NSOperationQueue जो enqueueOperation: बुला रहा है सभी उपलब्ध धागे का उपयोग कर रहा था बाहर कर देता है, तो जब से वे सब कुछ activeRequestsQueue पर होने के लिए (dispatch_sync के माध्यम से) इंतजार कर रहे हैं। इस कतार पर maxConcurrentOperations को कम करने से समस्या हल हो गई है (टिप्पणियां देखें), हालांकि यह वास्तव में एक अच्छा समाधान नहीं है क्योंकि यह कोर की संख्या आदि के बारे में धारणा करता है। dispatch_sync के बजाय dispatch_async का उपयोग करने के लिए एक बेहतर समाधान होगा, हालांकि यह कोड अधिक जटिल है।

मेरे पहले सुझाव:

  • आप dispatch_sync(activeRequestsQueue, ...) कॉल कर रहे हैं जब आप activeRequestsQueue की कगार पर हैं

    (और अपने ज़ोर किसी कारण से फायरिंग नहीं है, जैसे आप रिलीज में चला रहे हैं।)
  • [activeRequests removeObjectForKey:key]; एक अस्वीकार करने का अनुरोध कर रहा है, और डेलोक activeRequestForRpc: पर कॉल करने वाले किसी चीज़ की प्रतीक्षा कर रहा है, जो डेडलॉक का कारण बनता है।

+0

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

+0

यदि आपके पास डेलोक नहीं है, तो आरपीसीआरक्वेट में 'isEqual: 'या' हैश' जैसी विधियों के बारे में क्या है, जो शब्दकोश बाधा विधि में कॉल कर सकता है? –

+0

एनएसएमयूटेबलसेट में बदल गया है, इसलिए मैं अभी स्ट्रिंग्स को पढ़/लिख रहा/निकाल रहा हूं, आरपीसीआरक्वेट्स नहीं। मैं अभी भी वही व्यवहार देखता हूं। कतार की प्राथमिकता को बदलने से समस्या बहुत कम होती है (50% से लगभग 10% तक), लेकिन मुझे अभी भी यह 'dispatch_set_target_queue (activeRequestsQueue, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0)); ' –