2012-01-28 22 views
6

मुझे सी # में बहु थ्रेडिंग के साथ समस्या है। मैं किसी अन्य थ्रेड से किसी फ़ॉर्म में लेबल अपडेट करने के लिए किसी ईवेंट का उपयोग करता हूं, जिसके लिए मुझे निश्चित रूप से Invoke() कमांड का उपयोग करने की आवश्यकता होती है। वह हिस्सा भी ठीक काम कर रहा है। हालांकि, उपयोगकर्ता फॉर्म को बंद कर सकता है और यदि कोई दुर्भाग्यपूर्ण समय पर ईवेंट भेजा जाता है तो प्रोग्राम क्रैश हो सकता है।'डेडलॉक' केवल एक लॉक ऑब्जेक्ट के साथ?

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

हालांकि, हर बार जब मैं फॉर्म बंद करता हूं तो प्रोग्राम पूरी तरह से जम जाता है।

यहाँ कोड का उल्लेख किया भागों हैं:

private object dispose_lock = new object(); 
private bool _disposed = false; 

private void update(object sender, EventArgs e) 
{ 
    if (InvokeRequired) 
    { 
     EventHandler handler = new EventHandler(update); 
     lock (dispose_lock) 
     { 
      if (_disposed) return; 
      Invoke(handler); // this is where it crashes without using the lock 
     } 
     return; 
    } 

    label.Text = "blah"; 
} 

protected override void Dispose(bool disposing) 
{ 
    eventfullObject.OnUpdate -= update; 
    lock (dispose_lock) // this is where it seems to freeze 
    { 
     _disposed = true; // this is never called 
    } 
    base.Dispose(disposing); 
} 

मुझे आशा है कि यहाँ किसी को भी किसी भी विचार है कि इस कोड के साथ गलत है। अग्रिम धन्यवाद!

+0

असली आवेदन में एक अद्यतन कॉल विंडो निपटान करने के लिए पैदा कर सकता है? उस स्थिति में, पृष्ठभूमि थ्रेड में लॉक हो सकता है, और UI थ्रेड उसी ऑब्जेक्ट पर लॉकिंग का निपटान कर सकता है जिसमें बैकग्राउंड थ्रेड हो रहा है। –

+1

आप कहां परिवर्तनीय InvokeRequired प्राप्त कर रहे हैं, इसे उस नियंत्रण पर बुलाया जाना चाहिए जिसे आप अपडेट करना चाहते हैं, यानी: यदि (लेबल। इन्वोकर आवश्यक) {//} – Lloyd

उत्तर

1

मैं वास्तव में यहां सरल हो जाऊंगा। मुश्किल थ्रेड-सुरक्षित कोड को लागू करने के बजाय, मैं बस अपवाद को पकड़ूंगा और अगर यह विफल हो जाए तो कुछ भी नहीं करेगा।

मान लिया जाये कि यह एक ObjectDisposedException है:

try 
{ 
    this.Invoke(Invoke(handler)); 
} 
catch (ObjectDisposedException) 
{ 
    // Won't do anything here as 
    // the object is not in the good state (diposed when closed) 
    // so we can't invoke. 
} 

यह सरल और बिल्कुल स्पष्ट है। यदि कोई टिप्पणी निर्दिष्ट करती है तो आप अपवाद को पकड़ते हैं, मुझे लगता है कि यह ठीक है।

+1

खराब विचार आईएमओ ... – CodesInChaos

+0

@CodeInChaos मुझे नहीं लगता ताला की जटिलता, निपटान के ओवरराइड ... आदि। अपवाद को पकड़ने से बेहतर है। शायद आप समझा सकते हैं ** क्यों ** आपको लगता है कि यह एक बुरा विचार है ... – ken2k

+0

वही राय यहां: http://stackoverflow.com/a/1874785/870604 – ken2k

6

जो आप ध्यान में नहीं ले रहे हैं वह यह है कि प्रतिनिधि Invoke को यूआई थ्रेड पर असीमित रूप से कहा जाता है। कॉलिंग Invoke फ़ॉर्म संदेश कतार में एक संदेश पोस्ट करता है और कुछ समय बाद उठाया जाता है।

क्या होता है नहीं है:

UI Thread     Background Thread 
          Call update() 
          take lock 
          Call Invoke() 
Call update()    
          release lock 
Call Dispose() 
take lock 
release lock 

लेकिन बजाय:

UI Thread     Background Thread 
          Call update() 
           take lock 
           Call Invoke() 
           block until UI Thread processes the message 
Process messages 
... 
Dispose() 
    wait for lock ****** Deadlock! ***** 
... 
Call update()    
          release lock 
इस वजह से

, पृष्ठभूमि धागा जबकि यूआई धागा Dispose

को चलाने के लिए कोशिश कर रहा है ताला पकड़े जा धारण कर सकते हैं

समाधान आपके द्वारा किए गए प्रयास से कहीं अधिक सरल है। Invoke की वजह से अतुल्यकालिक रूप से पोस्ट किया गया है लॉक की कोई आवश्यकता नहीं है।

_ डिस्प्लेज्ड ध्वज केवल यूआई थ्रेड पर पढ़ा या लिखा जाता है इसलिए लॉक करने की कोई आवश्यकता नहीं है। अब आप फोन ढेर लगता है:

UI Thread     Background Thread 
          Call update() 
           take lock 
           Call Invoke() 
           block until UI Thread processes the message 
Process messages 
... 
Dispose() 
    _disposed = true; 
... 

Call update() 
    _disposed is true so do nothing    
+1

'Invoke' कॉल हमेशा असफल हो सकता है। कोड अभी भी एक अनजान अपवाद उत्पन्न करेगा जब नियंत्रण को सटीक समय पर बुलाया जाता है जब नियंत्रण का निपटारा किया जा रहा है – JaredPar

+0

Invoke हमेशा तुल्यकालिक है। – usr

+0

@usr जो मैं वर्णन करने की कोशिश कर रहा था वह था कि यूआई थ्रेड के संबंध में Invoke असीमित है। यह पृष्ठभूमि धागे के संबंध में तुल्यकालिक है। मैं अपना जवाब और स्पष्ट करने की कोशिश करूंगा – shf301

0

IMO Dispose बहुत देर हो चुकी है ...

मैं FormClosing जो पहले Dispose होता AFAIK कहा जाता है में कुछ कोड डाल सिफारिश करेंगे।

ऐसे मामले के लिए मैं आमतौर पर आपके चेक के लिए एक अलग (परमाणु) पैटर्न का उपयोग करता हूं - उदाहरण के लिए Interlocked कक्षा के माध्यम से।

private long _runnable = 1; 

private void update(object sender, EventArgs e) 
{ 
    if (InvokeRequired) 
    { 
     EventHandler handler = new EventHandler(update); 
     if (Interlocked.Read (ref _runnable) == 1) Invoke(handler); 
     return; 
    } 

    label.Text = "blah"; 
} 

FormClosing में आप सिर्फ Interlocked.Increment (ref _runnable) कहते हैं।

0

बस किसी भी अन्य उत्तर में अपराधी नहीं हैं, क्या कोई अन्य कोड है जो पोस्ट किए गए थ्रेड को समाप्त करता है? मैं आप सादे धागे का उपयोग किया जा सकता है और नहीं एक BackgroundWorker में सोच रहा हूँ, और सच

1

Control.Invoke का उपयोग कर के खतरों में से एक के लिए Thread.isBackround सेट करना भूल गए हैं कि यह के रूप में एक दुर्भाग्यपूर्ण समय में यूआई धागे पर निपटारा किया जा सकता है आपने सुझाव दिया सबसे आम तरीका ऐसा होता है जब आप घटनाओं

  1. पृष्ठभूमि थ्रेड के निम्न क्रम है: एक फोन आह्वान के साथ वापस आ Queues
  2. अग्रभूमि धागा: नियंत्रण निपटान जिस पर पृष्ठभूमि आह्वान
  3. अग्रभूमि धागा बुलाया : एक डिस्पोजेड कंट्रोल

इस परिदृश्य में Invoke विफल हो जाएगा और पृष्ठभूमि थ्रेड पर अपवाद का कारण बन जाएगा। यह संभवतः आपके आवेदन को पहली जगह दुर्घटनाग्रस्त कर रहा था।

नए कोड के साथ हालांकि यह एक मृत ताला का कारण बनता है। कोड लॉक को चरण # 1 में ले जाएगा। फिर यूआई में चरण # 2 पर निपटान होता है और यह लॉक की प्रतीक्षा कर रहा है जिसे चरण # 3 पूरा होने तक मुक्त नहीं किया जाएगा। इस जीत -

इस समस्या से निपटने के लिए सबसे आसान तरीका है स्वीकार करने के लिए कि Invoke एक ऑपरेशन और इसलिए असफल हो जायेगी कर सकते हैं एक try/catch

private void update(object sender, EventArgs e) 
{ 
    if (InvokeRequired) 
    { 
     EventHandler handler = new EventHandler(update); 
     try 
     { 
      Invoke(handler); 
     } 
     catch (Exception) 
     { 
      // Control disposed while invoking. Nothing to do 
     } 
     return; 
    } 

    label.Text = "blah"; 
} 
1

आप क्यों नहीं बस BeginInvoke बल्कि आह्वान से उपयोग नहीं करते हैं की जरूरत है पृष्ठभूमि धागे को ब्लॉक नहीं करते हैं। यह नहीं लगती है क्या कोई विशिष्ट कारण पृष्ठभूमि धागा यूआई अद्यतन क्या आप

1

से पता चला है एक और deadlocking परिदृश्य पैदा होती है जब Dispatcher.Invoke (बुला एक WPF आवेदन में से होने की प्रतीक्षा करने की जरूरत है की तरह) या नियंत्रण। इन्वोक (विंडोज़ फॉर्म एप्लिकेशन में) लॉक के कब्जे में रहते हुए। यदि यूआई एक और लॉक पर प्रतीक्षा कर रहे विधि को चलाने के लिए होता है, तो एक डेडलॉक सही होगा। इसे अक्सर InvIn के के बजाय BeginInvoke को कॉल करके ठीक किया जा सकता है। वैकल्पिक रूप से, आप पर कॉल करने से पहले अपना लॉक जारी कर सकते हैं, हालांकि आपका कॉलर लॉक निकालने पर यह काम नहीं करेगा। हम समृद्ध क्लाइंट अनुप्रयोगों और थ्रेड एफ़िनिटी में Invoke और BeginInvoke की व्याख्या करते हैं।

स्रोत: http://www.albahari.com/threading/part2.aspx

संबंधित मुद्दे