2008-12-02 10 views
25

मेरे पास मेरा मुख्य जीयूआई थ्रेड है, और दूसरा थ्रेड इसके अपने एप्लीकेशन कॉन्टेक्स्ट के अंदर चल रहा है (इसे जीवित रखने के लिए, यहां तक ​​कि जब कोई काम नहीं किया जाता है)। मैं अपने जीयूआई थ्रेड से अपने दूसरे थ्रेड पर एक विधि कॉल करना चाहता हूं, लेकिन अगर मैं सिर्फ थ्रेड कॉल करता हूं। विधि(); ऐसा लगता है कि यह मेरे मुख्य जीयूआई थ्रेड पर चल रहा है और मेरे जीयूआई को उत्तरदायी बनने का कारण बनता है। विभिन्न धागे पर तरीकों को कॉल करने का सबसे अच्छा तरीका क्या है?सी # में 2 धागे के बीच डेटा पास करने का अनुशंसित तरीका क्या है?

अद्यतन: जो मैं वास्तव में यहां करना चाहता हूं वह 2 धागे के बीच संवाद करता है, जीयूआई के साथ संवाद नहीं करता है। जीयूआई केवल उन धागे में से एक होता है जिन्हें मेरे दूसरे धागे से संवाद करने की आवश्यकता होगी।

अद्यतन # 2: ठीक है, मुझे वास्तव में कुछ याद आना चाहिए। मैंने एक कार्यक्रम और एक प्रतिनिधि बनाया और मेरे कार्यकर्ता धागे ने घटना की सदस्यता ली। लेकिन जब मैं Invoke (MyEvent) को कॉल करता हूं; मेरे जीयूआई थ्रेड से कार्यकर्ता धागा जीयूआई थ्रेड पर समाप्त होता है और जीयूआई थ्रेड को तब तक लटकता है जब तक यह प्रसंस्करण नहीं हो जाता है। क्या मैं एक स्थिर वस्तु पर मतदान किए बिना भी संभव करने की कोशिश कर रहा हूं?

उत्तर

12

नेट पहले से ही बुद्धि आता है एच System.ComponentModel.BackgroundWorker कक्षा विशेष रूप से प्रदर्शन कार्यों को संभालने और जीयूआई के साथ संवाद करने के लिए कक्षा। इसका इस्तेमाल करें।

+1

मैं अपने दूसरे धागे से जीयूआई के साथ संवाद करने की कोशिश नहीं कर रहा हूं, मैं जीयूआई से अपने धागे के साथ संवाद करने की कोशिश कर रहा हूं। –

+0

"एक जीयूआई के साथ संचार" का अर्थ दो-तरफा है, जीयूआई पृष्ठभूमिवर्कर थ्रेड के सार्वजनिक तरीकों को कॉल करने में सक्षम है। –

+0

हाँ, लेकिन यह वास्तव में "पृष्ठभूमि धागे के साथ संवाद नहीं करेगा" –

6

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

आपकी सर्वश्रेष्ठ शर्त वह है जो आप चाहते हैं और केवल .NET ThreadPool का उपयोग करें और इसे करने के लिए काम करें।

1

नए डेटा (या जीयूआई के नए राज्य) को संसाधित करने के लिए आवश्यक थ्रेड को सिग्नल करने के लिए सिंक्रनाइज़ेशन ऑब्जेक्ट का उपयोग करें। ऐसा करने का एक अपेक्षाकृत सरल तरीका एक ईवेंट ऑब्जेक्ट का उपयोग करना है। यहाँ है कि कैसे काम करेगा की एक विवरण दिया गया है:

  1. जीयूआई थ्रेड एक 2 धागा शेयर एक घटना वस्तु (ताकि वे दोनों इस बारे में पता)
  2. 2 धागा आम तौर पर किसी प्रकार की एक पाश में चलाता है, और प्रत्येक समय यह घटना के लिए इंतजार कर रहा है का संकेत किए जाने की
  3. जीयूआई धागा घटना का संकेत है जब यह कुछ
  4. जब 2 धागा किया जाता है, यह घटना रीसेट करता है और फिर से इंतजार कर रहा है (या निकास)
करने के लिए 2 धागा की जरूरत है
+1

आपको डेटा को रखने के लिए किसी प्रकार की कतार का उपयोग करने की आवश्यकता है - अन्यथा, प्रारंभिक धागा दूसरे थ्रेड पर डेटा भेजना चाहता है, लेकिन यह अभी भी पहले डेटा आइटम को संसाधित कर रहा है। आपको कतार को भी लॉक करने की आवश्यकता होगी, इसलिए वे एक ही समय में इसे एक्सेस करने का प्रयास नहीं करते हैं। – gbjbaanb

1

अपने दूसरे धागे में एक लूप डालें, जो ज्यादातर समय सोता है, लेकिन हर [अंतराल] यह जागता है और एक साझा चर को जांचता है जो बताता है कि आपकी विधि को चलाने के लिए या नहीं, और यदि वह साझा बूलियन सेट है सच में, फिर यह एक विधि चलाता है जो आप जो भी कार्य करने की कोशिश कर रहे हैं वह करता है ... उस विधि में, विधि किसी अन्य साझा चर से आवश्यक डेटा एकत्र करती है।

मुख्य जीयूआई धागा में, विधि पैरामीटर साझा चर में डेटा शब्दों में कहें, तो कार्यकर्ता विधि अंदर बूलियन "रन" सत्य पर साझा चर ...

सेट, साझा bool "रन पुनर्स्थापित करने के लिए याद "जब आप पूरा कर लेते हैं तो झूठी होती है, इसलिए लूप जीता जाता है; उसी उदाहरण को ओवर और ओवर चलाता है ...

+0

यह काम करेगा, लेकिन मुझे तत्काल होने के लिए विधि कॉल की आवश्यकता है। इसे कभी भी 1 एमएमएस या कुछ मतदान करना होगा, जो थोड़ा सा होगा। –

+0

फिर बस प्रतीक्षा थ्रेड लगातार चल रहा है। "थोड़ा अधिक" के बारे में आपकी चिंता के अनुसार, समझें कि आप यह कैसे करते हैं, प्रतीक्षा थ्रेड केवल ओएस द्वारा टाइम्सलाइस जारी किए जाने पर प्रतिक्रिया दे पाएगा, और यह तब हो सकता है जब कोड में चल रहा हो धागा जीवित है –

+0

एक साझा चर के लिए मतदान किए बिना ऐसा करने के लिए ब्रायन का सबसे अच्छा तरीका देखें। – gbjbaanb

3

मुझे लगता है कि जीयूआई में कुछ घटनाओं को चलाने के लिए कुछ लंबे समय तक चलने वाले काम की आवश्यकता है पृष्ठभूमि में - ऐसा करने के दो मुख्य तरीके हैं।यदि आप बस एक अलग थ्रेड पर एक विधि कॉल करना चाहते हैं तो आप इसे Calling Synchronous Methods Asynchronously द्वारा कर सकते हैं। मैं आमतौर पर कुछ इस तरह करते हैं: जब इस पद्धति है कि कॉलबैक पृष्ठभूमि धागे पर निष्पादित किया जाता है का उपयोग कर



//delegate with same prototype as the method to call asynchrously 
delegate void ProcessItemDelegate(object item); 

//method to call asynchronously 
private void ProcessItem(object item) { ... } 

//method in the GUI thread 
private void DoWork(object itemToProcess) 
{ 
    //create delegate to call asynchronously... 
    ProcessItemDelegate d = new ProcessItemDelegate(this.ProcessItem); 
    IAsyncResult result = d.BeginInvoke(itemToProcess, 
             new AsyncCallback(this.CallBackMethod), 
             d); 
} 

//method called when the async operation has completed 
private void CallbackMethod(IAsyncResult ar) 
{ 
    ProcessItemDelegate d = (ProcessItemDelegate)ar.AsyncState; 
    //EndInvoke must be called on any delegate called asynchronously! 
    d.EndInvoke(ar); 
} 

ध्यान रखें, तो अपडेट जीयूआई के लिए आह्वान का उपयोग किया जाना चाहिए।

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


//shared state 
private Queue workQueue; 
private EventWaitHandle eventHandle; 

//method running in gui thread 
private void DoWork(Item itemToProcess) 
{ 
    //use a private lock object instead of lock... 
    lock(this.workQueue) 
    { 
     this.workQueue.Add(itemToProcess); 
     this.eventHandle.Set(); 
    } 
} 

//method that runs on the background thread 
private void QueueMonitor() 
{ 
    while(keepRunning) 
    { 
     //if the event handle is not signalled the processing thread will sleep here until it is signalled or the timeout expires 
     if(this.eventHandle.WaitOne(optionalTimeout)) 
     { 
      lock(this.workQueue) 
      { 
       while(this.workQueue.Count > 0) 
       { 
       Item itemToProcess = this.workQueue.Dequeue(); 
       //do something with item... 
       } 
      } 
      //reset wait handle - note that AutoResetEvent resets automatically 
      this.eventHandle.Reset(); 
     } 
    } 
} 
+0

वास्तव में वहां एक दौड़ की स्थिति है। यदि आप QueueMonitor और ईवेंट रीसेट में लॉक खोलने के बीच DoWork कॉल करते हैं, तो आप एक ईवेंट याद करेंगे और अगले DoWork, या टाइमआउट तक अनब्लॉक नहीं करेंगे। – Jurney

2

Control.BeginInvoke() की सुविधा पास करना मुश्किल है। आपको नहीं करना है अपनी परियोजना के लिए एक नया वर्ग जोड़ें और इस कोड पेस्ट करें:

public partial class Form1 : Form { 
    private frmWorker mWorker; 
    public Form1() { 
     InitializeComponent(); 
     mWorker = new frmWorker(); 
    } 

    private void button1_Click(object sender, EventArgs e) { 
     Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); 
     mWorker.BeginInvoke(new MethodInvoker(RunThisOnThread)); 
    } 
    private void RunThisOnThread() { 
     Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); 
    } 

    private void button2_Click(object sender, EventArgs e) { 
     mWorker.Stop(); 
    } 
    } 
36

वाह, मैं विश्वास नहीं कर सकता कि कैसे लोगों को पढ़ने के लिए परेशान नहीं किया हो सकता है:

using System; 
using System.Threading; 
using System.Windows.Forms; 

public partial class frmWorker : Form { 
    public frmWorker() { 
    // Start the worker thread 
    Thread t = new Thread(new ParameterizedThreadStart(WorkerThread)); 
    t.IsBackground = true; 
    t.Start(this); 
    } 
    public void Stop() { 
    // Synchronous thread stop 
    this.Invoke(new MethodInvoker(stopWorker), null); 
    } 
    private void stopWorker() { 
    this.Close(); 
    } 
    private static void WorkerThread(object frm) { 
    // Start the message loop 
    frmWorker f = frm as frmWorker; 
    f.CreateHandle(); 
    Application.Run(f); 
    } 
    protected override void SetVisibleCore(bool value) { 
    // Shouldn't become visible 
    value = false; 
    base.SetVisibleCore(value); 
    } 
} 

यहाँ यह परीक्षण करने के लिए कुछ नमूना कोड सवाल।

वैसे भी, मैं यही करता हूं।

  1. आपको "संदेश" कक्षाएं बनाएं। यह वह सारी जानकारी संग्रहीत करता है जिसे आप साझा करना चाहते हैं।
  2. प्रत्येक धागे के लिए एक कतार < टी > बनाएं। इसे पढ़ने/लिखने के लिए सिंकलॉक (सी # लॉक) का उपयोग करें।
  3. जब आप किसी थ्रेड से बात करना चाहते हैं, तो उसे प्रति के साथ संदेश वस्तु को कतार में संदेश जोड़कर आवश्यक जानकारी के साथ भेजें।
  4. कार्यकर्ता थ्रेड कतार से पढ़ सकते हैं, क्रम में प्रत्येक संदेश को पढ़ और संसाधित कर सकते हैं। जब कोई संदेश नहीं होता है, तो बस सो जाओ।

सुनिश्चित करें कि आप दो धागे के बीच वस्तुओं को साझा नहीं करते हैं। एक बार जब आपका जीयूआई थ्रेड कतार में एक संदेश चिपकाता है, तो GUI थ्रेड अब संदेश का मालिक नहीं है। यह संदेश का संदर्भ नहीं रख सकता है, या आप खुद को परेशानी में डाल देंगे।

यह आपको सबसे अच्छा प्रदर्शन नहीं देगा, लेकिन यह अधिकांश अनुप्रयोगों के लिए पर्याप्त होगा। और सबसे महत्वपूर्ण बात यह है कि इससे गलती करना मुश्किल हो जाएगा।

अपडेट: सिंकलॉक और कतार का उपयोग न करें। इसके बजाय एक ConcurrentQueue का उपयोग करें, जो आपके लिए स्वचालित रूप से लॉकिंग को संभालेगा। आपको बेहतर प्रदर्शन मिलेगा और गलती करने की संभावना कम होगी।

+2

मुझे आश्चर्य है कि अन्य प्रतिक्रिया को जवाब के रूप में चिह्नित किया गया है। जोनाथन विधि का वर्णन हमेशा मेरे लिए कहीं अधिक प्रभावी साबित हुआ है। स्पष्ट कारणों से एनक्यू() या डेक्यू() में ले जाने से अधिक समय तक कतार को लॉक न करें। मामूली साइड-विषय पर, मैंने यह थ्रेड नेटवर्क संचार में तीन धागे (भेजें, प्राप्त करें, और प्रसंस्करण) और दो कतारों (प्राप्त => प्रसंस्करण, और प्रसंस्करण => भेजें) के साथ नेटवर्क संचार में बेहद प्रभावी पाया है। – Derek

1

आप घटनाओं का उपयोग कर सकते हैं या Grauenwolf ने कहा - एक संदेश क्यू। मैं अपने प्रत्येक धागे को एक प्रबंधन सिंगलटन के रूप में लपेटता हूं, वहां से आप आसानी से कार्यान्वित कर सकते हैं। बिट्स फ्लिप करने के लिए आप गरीब व्यक्ति सार्वजनिक संपत्ति भी कर सकते हैं।

इसके अलावा, आप एक राज्य मशीन को लागू कर सकता है, और पास संदेशों के स्थान पर प्रत्येक थ्रेड एक दूसरे को

+0

मुझे Grauenwolf के सुझाव पसंद आया, और उस और एक स्वीकार्य उत्तर का संयोजन कर समाप्त हो गया। –

6

Dude की निगरानी कर सकता है, मुक्त ebook सूत्रण Albahari के नेट पढ़ें। मैं किसी भी माध्यम से उससे जुड़ा हूं, इसलिए यह कोई प्लग नहीं है। मैंने इसे पढ़ लिया है, क्या मेरे सहकर्मी इसे पढ़ चुके थे, और मैंने इसे कई बार इस्तेमाल किया है।

मैं एक निर्माता/उपभोक्ता वर्ग बनाने की अनुशंसा करता हूं, जहां आप एक सिंगल थ्रेड शुरू कर सकते हैं जो (कूड़ेदान) की प्रतीक्षा करता है, कार्यों को अपनी कतार में लगाता है, और इसे काम शुरू करने के लिए संकेत देता है।

बस इसके लिए Google।

+0

बस इसे यहां छोड़ दें: http://www.albahari.com/threading/ – matterai

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

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