2010-02-28 26 views
6

मैं अपने सी # अनुप्रयोग को बहु थ्रेड करने की कोशिश कर रहा हूं क्योंकि कभी-कभी मुझे एक अपवाद मिलता है जो कहता है कि मैंने एक असुरक्षित तरीके से थ्रेड को कॉल किया है। मैंने प्रोग्राम में पहले कभी भी बहु-थ्रेडिंग नहीं की है, इसलिए अगर मैं इस मुद्दे पर अज्ञान महसूस करता हूं तो मेरे साथ सहन करें।विंडोज फॉर्म एप्लिकेशन में मल्टी-थ्रेडिंग कॉल?

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

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

private void runBtn_Click(object sender, EventArgs e) 
{ 
    // this is called when the user tells the program to launch the desired program and 
    // monitor it's CPU usage. 

    // sets up the process and performance counter 
    m.runAndMonitorApplication(); 

    // Create a new timer that runs every second, and gets CPU readings. 
    crntTimer = new System.Timers.Timer(); 
    crntTimer.Interval = 1000; 
    crntTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); 
    crntTimer.Enabled = true; 
} 

private void OnTimedEvent(object source, ElapsedEventArgs e) 
{ 
    // get the current processor time reading 
    float cpuReading = m.getCPUValue(); 

    // update the current cpu label 
    crntreadingslbl.Text = cpuReading.ToString(); // 

} 
// runs the application 
public void runAndMonitorApplication() 
{ 
    p = new Process(); 
    p.StartInfo.UseShellExecute = true; 
    p.StartInfo.CreateNoWindow = true; 
    p.StartInfo.FileName = fileName; 
    p.Start(); 

    pc = new System.Diagnostics.PerformanceCounter("Process", 
       "% Processor Time", 
       p.ProcessName, 
       true); 
} 

// This returns the current percentage of CPU utilization for the process 
public float getCPUValue() 
{ 
    float usage = pc.NextValue(); 

    return usage; 
} 

उत्तर

7

बहु सूत्रण पर जॉन स्कीट के लेख, multi-threading winforms पर विशेष रूप से पेज देखें। यह आपको ठीक से ठीक करना चाहिए।

असल में आपको यह देखने की आवश्यकता है कि कोई आविष्कार आवश्यक है या नहीं, और यदि आवश्यक हो तो इनवॉक करें। लेख को पढ़ने के बाद आप ब्लॉक कि इस तरह दिखना में अपने यूआई अद्यतन कोड refactor करने के लिए सक्षम होना चाहिए:

private void OnTimedEvent(object source, ElapsedEventArgs e) 
{ 
    // get the current processor time reading 
    float cpuReading = m.getCPUValue(); 

    if (InvokeRequired) 
    { 
     // We're not in the UI thread, so we need to call BeginInvoke 
     BeginInvoke(new Action(() => crntreadingslbl.Text = cpuReading.ToString())); 
     return; 
    } 
    // Must be on the UI thread if we've got this far 
    crntreadingslbl.Text = cpuReading.ToString(); 
} 

अपने कोड में, एक आह्वान क्योंकि आप एक टाइमर का उपयोग कर रहे आवश्यक हो जाएगा। System.Timers.Timer के लिए प्रलेखन के अनुसार:

विलम्बित घटना थ्रेडपूल थ्रेड पर उठाई गई है।

इसका मतलब है कि OnTimedEvent() विधि है कि आप टाइमर के प्रतिनिधि के रूप में सेट अगले उपलब्ध ThreadPool धागा है, जो निश्चित रूप से अपने यूआई धागा नहीं होगा पर अमल होगा।

आप इस तरह के एक फार्म या नियंत्रण के रूप में, एक यूजर इंटरफेस तत्व के साथ टाइमर का उपयोग करते हैं, आवंटित प्रपत्र या नियंत्रण कि करने के लिए टाइमर शामिल हैं: प्रलेखन भी इस समस्या को हल करने के लिए एक वैकल्पिक तरीका पता चलता है SynchronizingObject संपत्ति, ताकि ईवेंट को उपयोगकर्ता इंटरफ़ेस थ्रेड पर मार्शल किया गया हो।

आपको यह मार्ग आसान मिल सकता है, लेकिन मैंने कोशिश नहीं की है।

+0

ठीक है, यह और पृष्ठभूमि कार्यकर्ता टिप्पणी बहुत उपयोगी प्रतीत; लेकिन जैसा कि मैं इसे समझता हूं, प्रक्रिया यूआई थ्रेड पर चल रही है, लेकिन मुझे उस प्रक्रिया पर डेटा एकत्र करने और अपडेट करने के लिए एक अलग धागा बनाना है? आम तौर पर, मैं कैसे कह सकता हूं कि अलग धागा कहां बनाना है? – Waffles

+0

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

0

आपकी समस्या, मुझे लगता है, है इस लाइन है कि:

crntreadingslbl.Text = cpuReading.ToString(); 

यूआई धागे के बाहर चल रहा है। आप यूआई थ्रेड के बाहर यूआई तत्व अपडेट नहीं कर सकते हैं। UI थ्रेड पर एक नई विधि कॉल करने के लिए आपको विंडो पर Invoke कॉल करने की आवश्यकता है।

सभी ने कहा, क्यों perfmon का उपयोग नहीं करते? यह उद्देश्य के लिए बनाया गया है।

0

BackGroundWorker घटक आपकी मदद कर सकता है। यह टूलबॉक्स पर उपलब्ध है ताकि आप अपने फॉर्म पर खींच सकें।

यह घटक यूआई थ्रेड से अलग धागे में कार्यों को निष्पादित करने के लिए ईवेंट का एक सेट दिखाता है। आपको धागा बनाने के बारे में चिंता करने की ज़रूरत नहीं है।

पृष्ठभूमि और यूआई नियंत्रण पर चल रहे कोड के बीच सभी बातचीत ईवेंट हैंडलर के माध्यम से की जानी चाहिए।

अपने परिदृश्य के लिए आप एक विशिष्ट अंतराल पर पृष्ठभूमि कार्यकर्ता को ट्रिगर करने के लिए एक टाइमर सेट कर सकते हैं।

private void OnTimedEvent(object source, ElapsedEventArgs e) 
{ 
    backgroundWorker.RunWorkerAsync(); 
} 

तो फिर तुम उचित ईवेंट हैंडलर्स वास्तव में डेटा को इकट्ठा करने और अद्यतन करने के लिए यूआई को लागू

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    // Collect performance data and update the UI 
} 
संबंधित मुद्दे