2009-11-15 8 views
17

मैं बहु-थ्रेडिंग के साथ कुछ हद तक परिचित हूं जिसमें मैंने इसके बारे में पढ़ा है लेकिन अभ्यास में इसका कभी भी उपयोग नहीं किया है।सी # मल्टीथ्रेडिंग - बिना किसी नियंत्रण के आमंत्रित करें

मेरे पास एक प्रोजेक्ट है जो किसी तृतीय पक्ष लाइब्रेरी का उपयोग करता है जो ईवेंट को बढ़ाकर इनपुट डिवाइस की स्थिति साझा करता है। समस्या यह है कि जिस तरह से पुस्तकालय लिखा जाता है, इन घटनाओं को एक अलग धागे से उठाया जाता है।

मेरे आवेदन को बहु-थ्रेडेड होने की आवश्यकता नहीं है और मैंने कई क्लासिक थ्रेडिंग मुद्दों (यूआई नियंत्रणों को एक अलग थ्रेड से बातचीत करने के बारे में शिकायत करने के बारे में शिकायत की है, संग्रह जो कोड के एक टुकड़े के रूप में संशोधित होते हैं, पुनरावृत्त हो रहा है इसके ऊपर, आदि)।

मैं सिर्फ तीसरे पक्ष की लाइब्रेरी की घटना को अपने यूआई थ्रेड पर वापस देना चाहता हूं। विशेष रूप से मुझे लगता है कि क्या होना चाहिए:

मेरी कक्षा को ईवेंट प्राप्त होता है और यूआईएल की तुलना में हैंडलर एक अलग थ्रेड पर चल रहा है। मैं इस शर्त का पता लगाना चाहता हूं (जैसे InvokeRequired), और उसके बाद UI थ्रेड पर नियंत्रण देने के लिए BeginInvoke के बराबर प्रदर्शन करें। फिर कक्षा पदानुक्रम पर उचित अधिसूचनाएं भेजी जा सकती हैं और मेरा सभी डेटा केवल एक धागे से छूता है।

समस्या यह है कि, इन इनपुट घटनाओं को प्राप्त करने वाली कक्षा नियंत्रण से नहीं ली गई है और इसलिए InvokeRequired या BeginInvoke नहीं है। इसका कारण यह है कि मैंने यूआई और अंतर्निहित तर्क को साफ करने की कोशिश की। कक्षा अभी भी यूआई थ्रेड पर चल रही है, इसमें कक्षा के अंदर कोई यूआई नहीं है।

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

शायद कन्स्ट्रक्टर चलाने वाले थ्रेड के संदर्भ को सहेजने का कोई तरीका है और फिर थ्रेडिंग नेमस्पेस में कुछ ऐसा है जो Invoke कमांड करेगा?

क्या इसके आसपास कोई रास्ता है? क्या मेरा दृष्टिकोण पूरी तरह से गलत है?

उत्तर

18

AsyncOperation कक्षा में देखें। आप विधि का उपयोग करने पर हैंडलर को कॉल करने के लिए इच्छित थ्रेड पर AsyncOperation का उदाहरण बनाते हैं। तर्क जो मैं Create के लिए उपयोग करता हूं आमतौर पर शून्य है, लेकिन आप इसे किसी भी चीज़ पर सेट कर सकते हैं। उस धागे पर एक विधि को कॉल करने के लिए, AsyncOperation.Post विधि का उपयोग करें।

+0

मुझे यह समझने में थोड़ी देर लग गई कि प्रतिनिधियों का उपयोग कैसे किया जाता है लेकिन यह काम करता है! – colithium

1

आपको विशिष्ट नियंत्रण की आवश्यकता नहीं है, कोई भी नियंत्रण (फॉर्म सहित) करेगा। तो आप इसे कुछ हद तक यूआई से दूर कर सकते हैं।

2

हैंडलिंग विधि डेटा को केवल वर्ग के सदस्य चर में संग्रहीत कर सकती है। क्रॉस-थ्रेडिंग के साथ एकमात्र समस्या तब होती है जब आप थ्रेड को उस थ्रेड संदर्भ में बनाए गए नियंत्रणों को अद्यतन करना चाहते हैं। इसलिए, आपकी जेनेरिक क्लास घटना को सुन सकती है, और उसके बाद उस प्रतिनिधि नियंत्रण को आमंत्रित कर सकती है जिसे आप प्रतिनिधि फ़ंक्शन के साथ अपडेट करना चाहते हैं।

फिर से, केवल यूआई नियंत्रण जिन्हें आप अपडेट करना चाहते हैं उन्हें थ्रेड सुरक्षित बनाने के लिए बुलाया जाना चाहिए। कुछ समय पहले मैंने "Simple Solution to Illegal Cross-thread Calls in C#"

पर एक ब्लॉग एंट्री लिखी थी, पोस्ट अधिक विस्तार से चला गया है, लेकिन एक बहुत ही सरल (लेकिन सीमित) दृष्टिकोण का क्रूक्स यूआई नियंत्रण पर एक अज्ञात प्रतिनिधि समारोह का उपयोग करके है जिसे आप चाहते हैं अद्यतन:

if (label1.InvokeRequired) { 
    label1.Invoke(
    new ThreadStart(delegate { 
     label1.Text = "some text changed from some thread"; 
    })); 
} else { 
    label1.Text = "some text changed from the form's thread"; 
} 

मुझे उम्मीद है कि इससे मदद मिलती है। InvokeRequired तकनीकी रूप से वैकल्पिक है, लेकिन नियंत्रण का आविष्कार काफी महंगा है, ताकि यह सुनिश्चित हो सके कि यह लेबल 1 को अद्यतन नहीं करता है। अगर इसकी आवश्यकता नहीं है तो आवेदक के माध्यम से आगे बढ़ें।

+0

मेरे समस्या की तर्ज पर अधिक है: पुस्तकालय से कार्यकर्ता धागा मेरे ऐप को सूचित करता है और मैं तत्काल कार्रवाई करना चाहते हैं। मुद्दा यह है कि कार्यकर्ता धागे पर "तत्काल कार्रवाई" चलनी होगी, यूआई थ्रेड पर होने वाली अन्य चीजों को पूरी तरह से खोना होगा (चीजें जो यूआई संबंधित नहीं हैं - उदाहरण के लिए वस्तुओं की गणना सूची)। – colithium

0

क्या इसके आसपास कोई रास्ता है?

हां, आपके लिए एक थ्रेड-सुरक्षित कतार बनाने के लिए काम-आसपास होगा।

  • आपका ईवेंट हैंडलर 3 पक्ष के धागे से शुरू हो जाती है
  • आपका ईवेंट हैंडलर एक संग्रह पर कुछ (इवेंट डेटा) enqueues (जैसे एक सूची), जो आप
  • आपका ईवेंट हैंडलर ही संकेत करने के लिए कुछ करता है अपने स्वयं के थैड, कि संग्रह के लिए डेटा में डेक्यू और प्रक्रिया के लिए डेटा है:
    • आपका धागा कुछ (एक म्यूटेक्स या जो कुछ भी) पर इंतजार कर सकता है; जब उसके म्यूटेक्स को ईवेंट हैंडलर द्वारा संकेत दिया जाता है, तो यह उठता है और कतार की जांच करता है।
    • वैकल्पिक रूप से, संकेत किए जाने की बजाय, यह समय-समय पर जाग सकता है (उदाहरण के लिए प्रति सेकंड या जो भी हो) और कतार मतदान करें।

या तो मामले में, क्योंकि अपने कतार दो अलग धागे (3 पक्ष धागा enqueueing है, और अपने धागा dequeueing है) द्वारा लिखा जा रहा है, यह एक धागा सुरक्षित, संरक्षित कतार होने की जरूरत है ।

+0

मैंने इन पंक्तियों के साथ कई उदाहरण देखे।मैं गलत हो सकता हूं लेकिन यह तकनीक काम नहीं करेगी जब डेक्यूइंग मुख्य यूआई थ्रेड है जो सोने के लिए नहीं जा सकता है और वहां एक लूप में कतार को मतदान नहीं कर सकता है। मैंने इसका जिक्र नहीं किया लेकिन दूसरा धागा घटनाओं को कई बार एक बार बढ़ाता है। – colithium

+0

आप सही हैं: यह केवल तब ही अपरिवर्तित होगा यदि "आपका" धागा जिसे आप आमंत्रित करना चाहते थे * नहीं * यूआई थ्रेड था। – ChrisW

13

SynchronizationContext.Current का उपयोग करें, जो उस चीज़ को इंगित करेगा जिसे आप सिंक्रनाइज़ कर सकते हैं।

यह आवेदन के प्रकार के आधार पर ™ सही काम करेगा। WinForms एप्लिकेशन के लिए, यह इसे मुख्य यूआई थ्रेड पर चलाएगा।

विशेष रूप से, इस तरह, SynchronizationContext.Send विधि का उपयोग करें:

SynchronizationContext context = 
    SynchronizationContext.Current ?? new SynchronizationContext(); 

context.Send(s => 
    { 
     // your code here 
    }, null); 
+7

+1। ध्यान दें कि प्रोग्राम एक कंसोल एप्लिकेशन है, तो सिंक्रनाइज़ेशन कॉन्टेक्स्ट.कुरेंट' शून्य है। एक अच्छा पैटर्न 'AsyncOperation' का उपयोग कर रहा है जो सृजन पर वर्तमान' सिंक्रनाइज़ेशन कॉन्टेक्स्ट 'को कैप्चर करता है और * सही चीज़ ++ * ™ करता है। http://msdn.microsoft.com/en-us/library/system.componentmodel.asyncoperation.aspx – dtb

+0

उस वर्ग के बारे में पता नहीं था, मुझे बताने के लिए धन्यवाद। –

+0

यह WinForms है इसलिए यह काम करेगा लेकिन मेरा इरादा यूआई से स्वतंत्र होना था। कंसोल ऐप यूआई के संदर्भ में इसे दबा रहा है, मैं विचार करता हूं लेकिन यह जानना अच्छा है कि यह काम नहीं करेगा। अभी भी +1 – colithium

1

आप WPF उपयोग कर रहे हैं:

आप डिस्पैचर वस्तु जो यूआई धागा का प्रबंधन करता है के लिए एक संदर्भ की जरूरत है। फिर आप यूआई थ्रेड में होने वाले ऑपरेशन को शेड्यूल करने के लिए प्रेषक ऑब्जेक्ट पर Invoke या BeginInvoke विधि का उपयोग कर सकते हैं।

प्रेषक प्राप्त करने का सबसे आसान तरीका Application.Current.Dispatcher का उपयोग कर रहा है। यह प्रेषक मुख्य (और शायद एकमात्र) यूआई थ्रेड के लिए ज़िम्मेदार है।

यह सबको एक साथ रखें:

class MyClass 
{ 
    // Can be called on any thread 
    public ReceiveLibraryEvent(RoutedEventArgs e) 
    { 
     if (Application.Current.CheckAccess()) 
     { 
      this.ReceiveLibraryEventInternal(e); 
     } 
     else 
     { 
      Application.Current.Dispatcher.Invoke(
       new Action<RoutedEventArgs>(this.ReceiveLibraryEventInternal)); 
     } 
    } 

    // Must be called on the UI thread 
    private ReceiveLibraryEventInternal(RoutedEventArgs e) 
    { 
     // Handle event 
    } 
} 
+0

यह WPF के बारे में है, मुझे नहीं पता कि प्रश्न कहां इंगित करता है कि इसका उपयोग किया जाता है। –

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