दिलचस्प पोस्ट
अजाक्स के साथ चयन 4.0 4.0 प्रारंभिक मान बूटस्ट्रैप 4 संजाल navbar आइटम को सही पर सी ++ / सीएलआई में चार * और सिस्टम :: स्ट्रिंग के बीच कन्वर्ट करने का सबसे अच्छा तरीका क्या है? कॉलिगनिटर लाइब्रेरी के रूप में मेलजेट एपीआई v3 रैपर को एकीकृत करना क्या मैं सभी वस्तुओं की एक संकलन-सूची सूची प्राप्त कर सकता हूं जो स्काला में मुहरबंद माता-पिता से प्राप्त होती है? मैं एक पहले से खोला हुआ फ़ाइल कैसे खोलता हूं, जो कि नेट स्ट्रीम रीडर के साथ है? Django कस्टम फॉर्म पैरामीटर फॉर्मसेट को पास करना क्या मैं एक दृश्य से एक मॉडल को कॉल कर सकता हूं? एक जेनेरिक तरीकों पर एकाधिक वाइल्डकार्ड जावा संकलक (और मुझे!) बहुत ही भ्रमित हैं बाहरी जेएस स्क्रिप्ट गतिशील रूप से कोने 2 में लोड करें लिनक्स में डेमॉन बनाना संपादन टेक्स्ट के लिए कस्टम कट / प्रतिलिपि एक्शन बार जो टेक्स्ट चयन हैंडल दिखाता है जावास्क्रिप्ट में एस्केप उद्धरण क्यों विम विशेषज्ञों टैब पर बफ़र्स पसंद करते हैं? CSS3 के साथ इनसेट सीमा-त्रिज्या

सी # में मैं एक और थ्रेड से GUI कैसे अपडेट करूं?

किसी अन्य धागे से एक Label को अपडेट करने का सबसे आसान तरीका क्या है?

मेरे पास thread1 पर एक Form है, और उसके बाद से मैं एक धागा (धागा thread2 ) शुरू कर रहा हूं। जबकि thread2 कुछ फाइलों को संसाधित कर रहा है, मैं Form पर Label को thread2 के कार्य की वर्तमान स्थिति के साथ अद्यतन करना चाहता हूं।

मैं उसे कैसे कर सकता हूँ?

वेब के समाधान से एकत्रित समाधान "सी # में मैं एक और थ्रेड से GUI कैसे अपडेट करूं?"

.NET 2.0 के लिए, यहां मैंने लिखा कोड का एक अच्छा सा है जो आपको ठीक वही करता है, और Control पर किसी भी संपत्ति के लिए काम करता है:

 private delegate void SetControlPropertyThreadSafeDelegate( Control control, string propertyName, object propertyValue); public static void SetControlPropertyThreadSafe( Control control, string propertyName, object propertyValue) { if (control.InvokeRequired) { control.Invoke(new SetControlPropertyThreadSafeDelegate (SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue }); } else { control.GetType().InvokeMember( propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue }); } } 

इसे इस तरह कॉल करें:

 // thread-safe equivalent of // myLabel.Text = status; SetControlPropertyThreadSafe(myLabel, "Text", status); 

यदि आप .NET 3.0 या इसके बाद के संस्करण का उपयोग कर रहे हैं, तो आप उपरोक्त विधि को Control वर्ग की एक विस्तार विधि के रूप में दोबारा लिख ​​सकते हैं, जो तब कॉल को सरल करेगा:

 myLabel.SetPropertyThreadSafe("Text", status); 

05/10/2010 को अपडेट करें:

.NET 3.0 के लिए आपको इस कोड का उपयोग करना चाहिए:

 private delegate void SetPropertyThreadSafeDelegate<TResult>( Control @this, Expression<Func<TResult>> property, TResult value); public static void SetPropertyThreadSafe<TResult>( this Control @this, Expression<Func<TResult>> property, TResult value) { var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo; if (propertyInfo == null || !@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) || @this.GetType().GetProperty( propertyInfo.Name, propertyInfo.PropertyType) == null) { throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control."); } if (@this.InvokeRequired) { @this.Invoke(new SetPropertyThreadSafeDelegate<TResult> (SetPropertyThreadSafe), new object[] { @this, property, value }); } else { @this.GetType().InvokeMember( propertyInfo.Name, BindingFlags.SetProperty, null, @this, new object[] { value }); } } 

जो बहुत क्लीनर, सरल और सुरक्षित सिंटैक्स की अनुमति देने के लिए LINQ और लैम्ब्डा अभिव्यक्ति का उपयोग करता है:

 myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile 

न केवल संपत्ति नाम अब संकलन समय पर चेक किया गया है, संपत्ति का प्रकार भी है, इसलिए यह असंभव है (उदाहरण के लिए) एक बूलियन संपत्ति के लिए स्ट्रिंग मान निर्दिष्ट करें, और इसलिए एक रनटाइम अपवाद उत्पन्न करता है।

दुर्भाग्य से यह किसी को किसी अन्य Control की संपत्ति और मूल्य में गुजरने जैसी बेवकूफ चीज़ों को करने से रोक नहीं सकता है, इसलिए निम्न में खुशी से संकलित होगा:

 myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false); 

इसलिए मैंने यह सुनिश्चित करने के लिए रनटाइम चेक जोड़े हैं कि पारित-संपत्ति वास्तव में Control से संबंधित है, जिस पर विधि को बुलाया जा रहा है। सही नहीं, लेकिन अभी भी। NET 2.0 संस्करण की तुलना में बहुत बेहतर है।

यदि किसी के पास इस प्रकार के कोड को बेहतर बनाने के लिए कोई भी सुझाव है, तो कृपया कॉम्बल-टाइम सुरक्षा के लिए कृपया टिप्पणी करें!

सबसे सरल तरीका एक Label.Invoke विधि है जिसे Label.Invoke में दिया गया है:

 // Running on the worker thread string newText = "abc"; form.Label.Invoke((MethodInvoker)delegate { // Running on the UI thread form.Label.Text = newText; }); // Back on the worker thread 

सूचना है कि जब तक ब्लॉकों का निष्पादन पूरा नहीं हो जाता है – यह तुल्यकालिक कोड है। प्रश्न एसिंक्रोनस कोड के बारे में नहीं पूछता है, लेकिन जब आप इसके बारे में सीखना चाहते हैं तो एसिंक्रोनस कोड लिखने के बारे में स्टैक अतिप्रवाह पर बहुत सी सामग्री है

लंबे काम को संभालने

.NET 4.5 और C # 5.0 के बाद से आपको टास्क-आधारित एसिंक्रोनस पैटर्न (टीएपी) का उपयोग एसिंक के साथ करना चाहिए – सभी क्षेत्रों में खोजशब्दों का इंतजार (जीयूआई सहित):

टैप नए विकास के लिए अनुशंसित अतुल्यकालिक डिजाइन पैटर्न है

एसिंक्रोनस प्रोग्रामिंग मॉडल (एपीएम) और इवेंट-आधारित एसिंक्रोनस पैटर्न (ईएपी) के बजाय (बाद में पृष्ठभूमि वर्कर क्लास शामिल है )

फिर, नए विकास के लिए अनुशंसित समाधान है:

  1. किसी ईवेंट हैंडलर के अतुल्यकालिक क्रियान्वयन (हां, ये सब है):

     private async void Button_Clicked(object sender, EventArgs e) { var progress = new Progress<string>(s => label.Text = s); await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress), TaskCreationOptions.LongRunning); label.Text = "completed"; } 
  2. दूसरी थ्रेड का कार्यान्वयन जो UI थ्रेड को सूचित करता है:

     class SecondThreadConcern { public static void LongWork(IProgress<string> progress) { // Perform a long running work... for (var i = 0; i < 10; i++) { Task.Delay(500).Wait(); progress.Report(i.ToString()); } } } 

निम्नलिखित सूचनाएं:

  1. बिना कॉलबैक और स्पष्ट धागे के अनुक्रमिक तरीके से लिखे गए लघु और स्वच्छ कोड।
  2. थ्रेड के बजाय कार्य
  3. async कीवर्ड, जो कि इंतजार का उपयोग करने के लिए अनुमति देता है जो बदले में ईवेंट हैंडलर पूर्ण स्थिति तक पहुँचने तक कार्य समाप्त होने तक रोकता है और इस दौरान UI थ्रेड को ब्लॉक नहीं करता है।
  4. प्रगति वर्ग ( आईपीआरजीआई इंटरफ़ेस देखें) जो कि अलग-अलग चिंतनशीलता (एसओसी) डिजाइन सिद्धांत का समर्थन करता है और स्पष्ट डिस्पैचर की आवश्यकता नहीं है और इनका उपयोग नहीं करता है। यह अपने सृजन स्थल से वर्तमान सिंक्रनाइज़ेशन कंसटेक्स का उपयोग करता है (यहां UI थ्रेड)।
  5. कार्यक्रियाउपकरण। लंगनें उस कार्य को थ्रेडपूल में कतार में नहीं लाएं ।

एक और वर्बोज़ उदाहरणों के लिए देखें: द फ्यूचर ऑफ सी #: उन चीजों के लिए जो अच्छी बात है, जो यूसुफ एल्बहारी द्वारा 'प्रतीक्षा' करते हैं

UI थ्रेडिंग मॉडल अवधारणा के बारे में भी देखें

अपवादों को संभालना

नीचे दिए गए स्निपेट एक उदाहरण है कि अपवादों को कैसे संभालना है और पृष्ठभूमि की निष्पादन के दौरान एकाधिक क्लिकों को रोकने के लिए टॉगल बटन की Enabled संपत्ति है।

 private async void Button_Click(object sender, EventArgs e) { button.Enabled = false; try { var progress = new Progress<string>(s => button.Text = s); await Task.Run(() => SecondThreadConcern.FailingWork(progress)); button.Text = "Completed"; } catch(Exception exception) { button.Text = "Failed: " + exception.Message; } button.Enabled = true; } class SecondThreadConcern { public static void FailingWork(IProgress<string> progress) { progress.Report("I will fail in..."); Task.Delay(500).Wait(); for (var i = 0; i < 3; i++) { progress.Report((3 - i).ToString()); Task.Delay(500).Wait(); } throw new Exception("Oops..."); } } 

नेट ग्रेवेल के एनएटी 4 के लिए सबसे सरल समाधान के रूपांतर:

 control.Invoke((MethodInvoker) (() => control.Text = "new text")); 

या इसके बजाय एक्शन प्रतिनिधि का उपयोग करें:

 control.Invoke(new Action(() => control.Text = "new text")); 

दो की तुलना करने के लिए यहां देखें: Control.BeginInvoke के लिए MethodInvoker बनाम एक्शन

फायर और .NET 3.5+ के लिए एक्सटेंशन विधि भूल जाओ

 using System; using System.Windows.Forms; public static class ControlExtensions { /// <summary> /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread. /// </summary> /// <param name="control"></param> /// <param name="code"></param> public static void UIThread(this Control @this, Action code) { if (@this.InvokeRequired) { @this.BeginInvoke(code); } else { code.Invoke(); } } } 

इसे कोड की निम्न पंक्ति का उपयोग कर कहा जा सकता है:

 this.UIThread(() => this.myLabel.Text = "Text Goes Here"); 

यह क्लासिक तरीका है जिसे आप करना चाहिए:

 using System; using System.Windows.Forms; using System.Threading; namespace Test { public partial class UIThread : Form { Worker worker; Thread workerThread; public UIThread() { InitializeComponent(); worker = new Worker(); worker.ProgressChanged += new EventHandler<ProgressChangedArgs>(OnWorkerProgressChanged); workerThread = new Thread(new ThreadStart(worker.StartWork)); workerThread.Start(); } private void OnWorkerProgressChanged(object sender, ProgressChangedArgs e) { // Cross thread - so you don't get the cross-threading exception if (this.InvokeRequired) { this.BeginInvoke((MethodInvoker)delegate { OnWorkerProgressChanged(sender, e); }); return; } // Change control this.label1.Text = e.Progress; } } public class Worker { public event EventHandler<ProgressChangedArgs> ProgressChanged; protected void OnProgressChanged(ProgressChangedArgs e) { if(ProgressChanged!=null) { ProgressChanged(this,e); } } public void StartWork() { Thread.Sleep(100); OnProgressChanged(new ProgressChangedArgs("Progress Changed")); Thread.Sleep(100); } } public class ProgressChangedArgs : EventArgs { public string Progress {get;private set;} public ProgressChangedArgs(string progress) { Progress = progress; } } } 

आपके कार्यकर्ता थ्रेड में कोई इवेंट है आपका यूआई धागा काम करने के लिए दूसरे धागे से शुरू होता है और उस कार्यकर्ता घटना को हुक करता है ताकि आप कार्यकर्ता थ्रेड की स्थिति प्रदर्शित कर सकें।

फिर UI में आपको वास्तविक नियंत्रण बदलने के लिए सूत्रों को पार करना होगा … जैसे लेबल या प्रगति बार

सरल समाधान Control.Invoke का उपयोग करने के लिए है।

 void DoSomething() { if (InvokeRequired) { Invoke(new MethodInvoker(updateGUI)); } else { // Do Something updateGUI(); } } void updateGUI() { // update gui here } 

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

  private void button1_Click(object sender, EventArgs e) { backgroundWorker1.WorkerReportsProgress = true; backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { Thread.Sleep(5000); backgroundWorker1.ReportProgress(0, "A"); Thread.Sleep(5000); backgroundWorker1.ReportProgress(0, "B"); Thread.Sleep(5000); backgroundWorker1.ReportProgress(0, "C"); } private void backgroundWorker1_ProgressChanged( object sender, ProgressChangedEventArgs e) { label1.Text = e.UserState.ToString(); } 

यह ठीक है यदि आप हमेशा उसी फ़ील्ड को अपडेट करना चाहते हैं। यदि आपको अधिक जटिल अपडेट मिलते हैं, तो आप UI स्थिति को दर्शाने के लिए एक क्लास को परिभाषित कर सकते हैं और इसे ReportProgress विधि पर पास कर सकते हैं।

एक अंतिम बात, WorkerReportsProgress ध्वज सेट करना सुनिश्चित करें, या ReportProgress WorkerReportsProgress विधि को पूरी तरह से अनदेखा कर दिया जाएगा।

उत्तर के विशाल बहुमत Control.Invoke उपयोग करते हैं। Control.Invoke जो एक दौड़ की स्थिति होने का इंतज़ार कर रहा है । उदाहरण के लिए, स्वीकृत उत्तर पर विचार करें:

 string newText = "abc"; // running on worker thread this.Invoke((MethodInvoker)delegate { someLabel.Text = newText; // runs on UI thread }); 

यदि उपयोगकर्ता इस से पहले ही फॉर्म को बंद कर देता है। this.Invoke कहलाता है (याद रखें, this Form ऑब्जेक्ट है), एक ऑब्जेक्ट ObjectDisposedException की संभावना ObjectDisposedException

समाधान, SynchronizationContext , विशेष रूप से SynchronizationContext.Current कंटैक्स। का उपयोग करने के लिए हैमिल्टन डैनियलबैब के रूप में संदर्भ देता है (अन्य जवाब विशिष्ट SynchronizationContext लागूकरण पर निर्भर करते हैं जो पूरी तरह से अनावश्यक है)। मैं SynchronizationContext.Post बजाय SynchronizationContext.Post का उपयोग करने के लिए थोड़ी सी संकेतन को संशोधित कर सकता हूं। हालांकि (जैसा कि कर्मचारी को प्रतीक्षा करने के लिए आमतौर पर कोई ज़रूरत नहीं है):

 public partial class MyForm : Form { private readonly SynchronizationContext _context; public MyForm() { _context = SynchronizationContext.Current ... } private MethodOnOtherThread() { ... _context.Post(status => someLabel.Text = newText,null); } } 

ध्यान दें कि .NET 4.0 और ऊपर पर आप वास्तव में एसिंक कार्यों के लिए कार्य का उपयोग करना चाहिए। समकक्ष कार्य-आधारित दृष्टिकोण के लिए n-san का जवाब देखें ( TaskScheduler.FromCurrentSynchronizationContext का उपयोग TaskScheduler.FromCurrentSynchronizationContext )

अंत में, .NET 4.5 और ऊपर आप Progress<T> उपयोग भी कर सकते हैं Progress<T> (जो कि मूल रूप से SynchronizationContext.Current कंसोर्ट। को अपनी रचना पर कब्जा करता है) के रूप में Ryszard Dżegan द्वारा उन मामलों के लिए दिखाया गया है जहां लंबे समय से चलने वाले ऑपरेशन को अभी भी काम करते समय UI कोड चलाने की आवश्यकता होती है।

आपको यह सुनिश्चित करना होगा कि अपडेट सही धागा पर होता है; UI थ्रेड

ऐसा करने के लिए, आपको इसे सीधे कॉल करने के बजाय ईवेंट-हैंडलर को शामिल करना होगा।

आप ऐसा अपने घटना को बढ़ाकर कर सकते हैं:

(यह कोड मेरे सिर से बाहर लिखा गया है, इसलिए मैंने सही वाक्यविन्यास आदि के लिए जाँच नहीं की है, लेकिन इसे आपको जाना चाहिए।)

 if( MyEvent != null ) { Delegate[] eventHandlers = MyEvent.GetInvocationList(); foreach( Delegate d in eventHandlers ) { // Check whether the target of the delegate implements // ISynchronizeInvoke (Winforms controls do), and see // if a context-switch is required. ISynchronizeInvoke target = d.Target as ISynchronizeInvoke; if( target != null && target.InvokeRequired ) { target.Invoke (d, ... ); } else { d.DynamicInvoke ( ... ); } } } 

ध्यान दें कि उपरोक्त कोड WPF परियोजनाओं पर काम नहीं करेगा, क्योंकि WPF नियंत्रण ISynchronizeInvoke इंटरफ़ेस को लागू नहीं करता है।

यह सुनिश्चित करने के लिए कि ऊपर दिए गए कोड, विंडोज फॉर्म और डब्ल्यूपीएफ, और अन्य सभी प्लेटफॉर्म्स के साथ काम करता है, आप AsyncOperation , AsyncOperationManager और SynchronizationContext AsyncOperationManager क्लासेस को देख सकते हैं।

इस तरह से घटनाओं को आसानी से बढ़ाने के लिए, मैंने एक एक्सटेंशन विधि बनाई है, जिससे मुझे कॉल करने से एक घटना को बढ़ाने में मदद मिलती है:

 MyEvent.Raise(this, EventArgs.Empty); 

बेशक, आप बैकग्राउंड वर्कर क्लास का भी उपयोग कर सकते हैं, जो आपके लिए यह विषय सारंग होगा।

आपको GUI थ्रेड पर विधि को शामिल करने की आवश्यकता होगी। आप नियंत्रण करके कॉल कर सकते हैं। इन्वोक

उदाहरण के लिए:

 delegate void UpdateLabelDelegate (string message); void UpdateLabel (string message) { if (InvokeRequired) { Invoke (new UpdateLabelDelegate (UpdateLabel), message); return; } MyLabelControl.Text = message; } 

पिछली उत्तर में सामानों को शामिल करने में से कोई भी आवश्यक नहीं है

आपको WindowsFormsSynchronizationContext को देखने की आवश्यकता है:

 // In the main thread WindowsFormsSynchronizationContext mUiContext = new WindowsFormsSynchronizationContext(); ... // In some non-UI Thread // Causes an update in the GUI thread. mUiContext.Post(UpdateGUI, userData); ... void UpdateGUI(object userData) { // Update your GUI controls here } 

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

 public class MyForm : Form { private volatile string m_Text = ""; private System.Timers.Timer m_Timer; private MyForm() { m_Timer = new System.Timers.Timer(); m_Timer.SynchronizingObject = this; m_Timer.Interval = 1000; m_Timer.Elapsed += (s, a) => { MyProgressLabel.Text = m_Text; }; m_Timer.Start(); var thread = new Thread(WorkerThread); thread.Start(); } private void WorkerThread() { while (...) { // Periodically publish progress information. m_Text = "Still working..."; } } } 

ISynchronizeInvoke.Invoke और ISynchronizeInvoke.BeginInvoke विधियों का उपयोग करते समय आवश्यक मार्शलिंग ऑपरेशन को ISynchronizeInvoke.Invoke । मार्शलिंग तकनीक का उपयोग करने में कुछ भी गलत नहीं है, लेकिन कुछ ऐसे चेतावनियां हैं जिन्हें आपको पता होना चाहिए।

  • सुनिश्चित करें कि आप BeginInvoke बार-बार फोन नहीं करते हैं या यह संदेश पंप को ऊंचा कर सकता है
  • कार्यकर्ता थ्रेड पर कॉलिंग कॉल करना एक अवरुद्ध कॉल है। यह उस थ्रेड में किया जा रहा काम को अस्थायी रूप से रोक देगा।

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

  • UI और कार्यकर्ता थ्रेड्स Control.Invoke विरोध में ढीले युग्मित रहते हैं। Control.Invoke या Control.BeginInvokeControl.Invoke Control.BeginInvoke दृष्टिकोण जो कसकर जोड़े हैं
  • यूआई थ्रेड वर्कर थ्रेड की प्रगति को बाधित नहीं करेगा।
  • कार्यकर्ता थ्रेड उस समय पर हावी नहीं हो सकता जब UI धागा अद्यतन को खर्च करता है।
  • अंतराल, जिस पर यूआई और कार्यकर्ता थ्रेड्स कार्य करते हैं, वे स्वतंत्र रह सकते हैं।
  • कार्यकर्ता थ्रेड UI थ्रेड के संदेश पंप को ऊंचा नहीं कर सकता।
  • UI थ्रेड को यह निर्देश देना होता है कि कब और कितनी बार यूआई को अपडेट किया जाता है

कई उद्देश्यों के लिए यह उतना सरल है:

 public delegate void serviceGUIDelegate(); private void updateGUI() { this.Invoke(new serviceGUIDelegate(serviceGUI)); } 

"serviceGUI ()" एक जीयूआई स्तरीय विधि है, जिस प्रकार (यह) जो कि आप जितनी चाहें बदल सकते हैं। अन्य थ्रेड से "updateGUI ()" कॉल करें मानों को पारित करने के लिए पैरामीटर जोड़ा जा सकता है, और (संभवतः तेज़) उन पर ताले के साथ वर्ग के दायरे चर का उपयोग करके आवश्यक हो सकता है अगर उनसे प्रवेश करने वाले थ्रेड्स के बीच संघर्ष की कोई संभावना है जो अस्थिरता का कारण बन सकती है यदि गैर-जीयूआई थ्रेड समय महत्वपूर्ण है, तो Invoke के बजाय BeginInvoke का उपयोग करें (ब्रायन गिदोन की चेतावनी को ध्यान में रखते हुए)

यह इयान केम्प के समाधान के अपने सी # 3.0 भिन्नरूप में है:

 public static void SetPropertyInGuiThread<C,V>(this C control, Expression<Func<C, V>> property, V value) where C : Control { var memberExpression = property.Body as MemberExpression; if (memberExpression == null) throw new ArgumentException("The 'property' expression must specify a property on the control."); var propertyInfo = memberExpression.Member as PropertyInfo; if (propertyInfo == null) throw new ArgumentException("The 'property' expression must specify a property on the control."); if (control.InvokeRequired) control.Invoke( (Action<C, Expression<Func<C, V>>, V>)SetPropertyInGuiThread, new object[] { control, property, value } ); else propertyInfo.SetValue(control, value, null); } 

आप इसे इस तरह कहते हैं:

 myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!") 
  1. यह "सदस्य अभिव्यक्ति के रूप में" परिणाम के लिए निरर्थक जांच जोड़ता है
  2. यह स्थिर प्रकार-सुरक्षा को सुधारता है

अन्यथा, मूल एक बहुत अच्छा समाधान है।

 Label lblText; //initialized elsewhere void AssignLabel(string text) { if (InvokeRequired) { BeginInvoke((Action<string>)AssignLabel, text); return; } lblText.Text = text; } 

नोट करें कि BeginInvoke() से पसंदीदा है क्योंकि यह डेडलॉक होने की संभावना कम है (हालांकि, यह केवल एक लेबल पर पाठ निर्दिष्ट करते समय एक समस्या नहीं है):

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

यह वास्तव में हमारे कुछ रिहा सॉफ़्टवेयर को लटका देने के कारण हुआ BeginInvoke() साथ Invoke() को बदलकर ठीक करना आसान था जब तक आपको सिंक्रोनस ऑपरेशन की आवश्यकता न हो, जो कि अगर आपको रिटर्न वैल्यू की आवश्यकता होती है, तो हो सकता है कि BeginInvoke() उपयोग करें।

Salvete! इस सवाल के लिए खोज करने के बाद, मुझे फ्रैंकजी और ओरेगन भूत द्वारा दिए गए उत्तर मेरे लिए सबसे आसान सबसे उपयोगी साबित हुए। अब, मैं विजुअल बेसिक में कोड और एक कनवर्टर के माध्यम से इस स्निपेट को चलाया; इसलिए मुझे यकीन नहीं है कि यह कैसे मुड़ता है।

मेरे पास एक संवाद प्रपत्र है जिसे form_Diagnostics, कहा जाता है form_Diagnostics, जिसमें एक updateDiagWindow, बॉक्स है, जिसे updateDiagWindow, कहा जाता है updateDiagWindow, जिसे मैं लॉगिंग डिस्प्ले के एक प्रकार के रूप में उपयोग कर रहा हूं। मुझे अपने पाठ को सभी धागे से अपडेट करने में सक्षम होना चाहिए। अतिरिक्त लाइनें विंडो को स्वचालित रूप से नवीनतम लाइनों तक स्क्रॉल करने की अनुमति देती हैं

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

  form_Diagnostics.updateDiagWindow(whatmessage); 

मुख्य संहिता (इसे अपने फ़ॉर्म के वर्ग कोड के अंदर डालें):

 #region "---------Update Diag Window Text------------------------------------" // This sub allows the diag window to be updated by all threads public void updateDiagWindow(string whatmessage) { var _with1 = diagwindow; if (_with1.InvokeRequired) { _with1.Invoke(new UpdateDiagDelegate(UpdateDiag), whatmessage); } else { UpdateDiag(whatmessage); } } // This next line makes the private UpdateDiagWindow available to all threads private delegate void UpdateDiagDelegate(string whatmessage); private void UpdateDiag(string whatmessage) { var _with2 = diagwindow; _with2.appendtext(whatmessage); _with2.SelectionStart = _with2.Text.Length; _with2.ScrollToCaret(); } #endregion 

यह एक। NET Framework 3.0 का उपयोग कर ऊपर समाधान के समान है, लेकिन यह संकलन-समय सुरक्षा समर्थन के मुद्दे को हल किया।

 public static class ControlExtension { delegate void SetPropertyValueHandler<TResult>(Control souce, Expression<Func<Control, TResult>> selector, TResult value); public static void SetPropertyValue<TResult>(this Control source, Expression<Func<Control, TResult>> selector, TResult value) { if (source.InvokeRequired) { var del = new SetPropertyValueHandler<TResult>(SetPropertyValue); source.Invoke(del, new object[]{ source, selector, value}); } else { var propInfo = ((MemberExpression)selector.Body).Member as PropertyInfo; propInfo.SetValue(source, value, null); } } } 

काम में लाना:

 this.lblTimeDisplay.SetPropertyValue(a => a.Text, "some string"); this.lblTimeDisplay.SetPropertyValue(a => a.Visible, false); 

कंपाइलर विफल होगा यदि उपयोगकर्ता गलत डेटा प्रकार पास करता है।

 this.lblTimeDisplay.SetPropertyValue(a => a.Visible, "sometext"); 

जब मुझे एक ही मुद्दे का सामना करना पड़ा तो मैंने Google से सहायता मांगी, लेकिन मुझे एक सरल समाधान देने की बजाय मुझे MethodInvoker और ब्ला ब्ला ब्ला के उदाहरण देकर मुझे और भ्रमित कर दिया। इसलिए मैंने इसे खुद ही हल करने का फैसला किया। यहां मेरा समाधान है:

इस तरह एक प्रतिनिधि बनाओ:

 Public delegate void LabelDelegate(string s); void Updatelabel(string text) { if (label.InvokeRequired) { LabelDelegate LDEL = new LabelDelegate(Updatelabel); label.Invoke(LDEL, text); } else label.Text = text } 

You can call this function in a new thread like this

 Thread th = new Thread(() => Updatelabel("Hello World")); th.start(); 

Don't be confused with Thread(() => .....) . I use an anonymous function or lambda expression when I work on a thread. To reduce the lines of code you can use the ThreadStart(..) method too which I am not supposed to explain here.

Simply use something like this:

  this.Invoke((MethodInvoker)delegate { progressBar1.Value = e.ProgressPercentage; // runs on UI thread }); 

You may use the already-existing delegate Action :

 private void UpdateMethod() { if (InvokeRequired) { Invoke(new Action(UpdateMethod)); } } 

You must use invoke and delegate

 private delegate void MyLabelDelegate(); label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; }); 

My version is to insert one line of recursive "mantra":

For no arguments:

  void Aaaaaaa() { if (InvokeRequired) { Invoke(new Action(Aaaaaaa)); return; } //1 line of mantra // Your code! } 

For a function that has arguments:

  void Bbb(int x, string text) { if (InvokeRequired) { Invoke(new Action<int, string>(Bbb), new[] { x, text }); return; } // Your code! } 

THAT is IT .


Some argumentation : Usually it is bad for code readability to put {} after an if () statement in one line. But in this case it is routine all-the-same "mantra". It doesn't break code readability if this method is consistent over the project. And it saves your code from littering (one line of code instead of five).

As you see if(InvokeRequired) {something long} you just know "this function is safe to call from another thread".

Create a class variable:

 SynchronizationContext _context; 

Set it in the constructor that creates your UI:

 var _context = SynchronizationContext.Current; 

When you want to update the label:

 _context.Send(status =>{ // UPDATE LABEL }, null); 

Try to refresh the label using this

 public static class ExtensionMethods { private static Action EmptyDelegate = delegate() { }; public static void Refresh(this UIElement uiElement) { uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate); } } 

The easiest way I think:

  void Update() { BeginInvoke((Action)delegate() { //do your update }); } 

For example, access a control other than in the current thread:

 Speed_Threshold = 30; textOutput.Invoke(new EventHandler(delegate { lblThreshold.Text = Speed_Threshold.ToString(); })); 

There the lblThreshold is a Label and Speed_Threshold is a global variable.

I just read the answers and this appears to be a very hot topic. I'm currently using .NET 3.5 SP1 and Windows Forms.

The well-known formula greatly described in the previous answers that makes use of the InvokeRequired property covers most of the cases, but not the entire pool.

What if the Handle has not been created yet?

The InvokeRequired property, as described here (Control.InvokeRequired Property reference to MSDN) returns true if the call was made from a thread that is not the GUI thread, false either if the call was made from the GUI thread, or if the Handle was not created yet.

You can come across an exception if you want to have a modal form shown and updated by another thread. Because you want that form shown modally, you could do the following:

 private MyForm _gui; public void StartToDoThings() { _gui = new MyForm(); Thread thread = new Thread(SomeDelegate); thread.Start(); _gui.ShowDialog(); } 

And the delegate can update a Label on the GUI:

 private void SomeDelegate() { // Operations that can take a variable amount of time, even no time //... then you update the GUI if(_gui.InvokeRequired) _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; }); else _gui.Label1.Text = "Done!"; } 

This can cause an InvalidOperationException if the operations before the label's update "take less time" (read it and interpret it as a simplification) than the time it takes for the GUI thread to create the Form 's Handle . This happens within the ShowDialog() method.

You should also check for the Handle like this:

 private void SomeDelegate() { // Operations that can take a variable amount of time, even no time //... then you update the GUI if(_gui.IsHandleCreated) // <---- ADDED if(_gui.InvokeRequired) _gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; }); else _gui.Label1.Text = "Done!"; } 

You can handle the operation to perform if the Handle has not been created yet: You can just ignore the GUI update (like shown in the code above) or you can wait (more risky). This should answer the question.

Optional stuff: Personally I came up coding the following:

 public class ThreadSafeGuiCommand { private const int SLEEPING_STEP = 100; private readonly int _totalTimeout; private int _timeout; public ThreadSafeGuiCommand(int totalTimeout) { _totalTimeout = totalTimeout; } public void Execute(Form form, Action guiCommand) { _timeout = _totalTimeout; while (!form.IsHandleCreated) { if (_timeout <= 0) return; Thread.Sleep(SLEEPING_STEP); _timeout -= SLEEPING_STEP; } if (form.InvokeRequired) form.Invoke(guiCommand); else guiCommand(); } } 

I feed my forms that get updated by another thread with an instance of this ThreadSafeGuiCommand , and I define methods that update the GUI (in my Form) like this:

 public void SetLabeTextTo(string value) { _threadSafeGuiCommand.Execute(this, delegate { Label1.Text = value; }); } 

In this way I'm quite sure that I will have my GUI updated whatever thread will make the call, optionally waiting for a well-defined amount of time (the timeout).

I wanted to add a warning because I noticed that some of the simple solutions omit the InvokeRequired check.

I noticed that if your code executes before the window handle of the control has been created (eg before the form is shown), Invoke throws an exception. So I recommend always checking on InvokeRequired before calling Invoke or BeginInvoke .

Even if the operation is time-consuming (thread.sleep in my example) – This code will NOT lock your UI:

  private void button1_Click(object sender, EventArgs e) { Thread t = new Thread(new ThreadStart(ThreadJob)); t.IsBackground = true; t.Start(); } private void ThreadJob() { string newValue= "Hi"; Thread.Sleep(2000); this.Invoke((MethodInvoker)delegate { label1.Text = newValue; }); }