Wenn Sie Multithreading verwenden, um die Leistung Ihrer Windows Forms-Anwendung zu verbessern, müssen Sie sicherstellen, dass Steuerelemente threadsicher aufgerufen werden.
Der Zugriff auf Windows Forms-Steuerelemente ist nicht grundsätzlich threadsicher. Wenn zwei oder mehr Threads den Status eines Steuerelements manipulieren, kann es sein, dass das Steuerelement in einen inkonsistenten Status versetzt wird. Es können andere Thread-bezogene Fehler auftreten, z. B. Race Conditions und Deadlocks. Stellen Sie sicher, dass Sie auf Thread-sichere Weise auf Steuerelemente zugreifen.
1. Probleme, auf die Anfänger häufig stoßen
Es ist unsicher, ein Steuerelement von einem Thread aus aufzurufen, der noch nie die Invoke-Methode zum Erstellen des Steuerelements verwendet hat. Unten finden Sie ein Beispiel für einen nicht threadsicheren Aufruf. Während der Laufzeit wird eine InvalidOperationException-Meldung mit der Fehlermeldung „Auf den Steuerelementnamen wird von einem Thread zugegriffen, der das Steuerelement nicht erstellt hat“ ausgegeben.
// This event handler creates a thread that calls a // Windows Forms control in an unsafe way.private void setTextUnsafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcUnsafe)); this.demoThread.Start(); }// This method is executed on the worker thread and makes// an unsafe call on the TextBox control.private void ThreadProcUnsafe() { this.textBox1.Text = "This text was set unsafely."; }
2. Lösung
So führen Sie threadsichere Aufrufe an Windows Forms-Steuerelemente durch.
①Fragen Sie die InvokeRequired-Eigenschaft des Steuerelements ab.
②Wenn InvokeRequired „true“ zurückgibt, verwenden Sie den Delegaten des eigentlichen aufrufenden Steuerelements, um Invoke aufzurufen.
③Wenn InvokeRequired false zurückgibt, rufen Sie bitte das Steuerelement direkt auf.
Hier sind sie in synchrone Ausführungsdelegation und asynchrone Ausführungsdelegation unterteilt.
Im folgenden Codebeispiel wird ein Thread-sicherer Aufruf in der ThreadProcSafe-Methode implementiert, der von einem Hintergrundthread ausgeführt wird. Wenn InvokeRequired des TextBox-Steuerelements „true“ zurückgibt, erstellt die ThreadProcSafe-Methode eine SetTextCallback-Instanz und übergibt sie an die Invoke-Methode des Formulars. Dadurch wird die SetText-Methode für den Thread aufgerufen, der das TextBox-Steuerelement erstellt hat, und die Text-Eigenschaft wird direkt im Kontext dieses Threads festgelegt.
// This event handler creates a thread that calls a // Windows Forms control in a thread-safe way. private void setTextSafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe)); this.demoThread.Start(); }// This method is executed on the worker thread and makes // a thread-safe call on the TextBox control. private void ThreadProcSafe() { this.SetText("This text was set safely."); }// This delegate enables asynchronous calls for setting // the text property on a TextBox control.delegate void SetTextCallback(string text); // This method demonstrates a pattern for making thread-safe // calls on a Windows Forms control. // // If the calling thread is different from the thread that // created the TextBox control, this method creates a // SetTextCallback and calls itself asynchronously using the// Invoke method. // // If the calling thread is the same as the thread that created // the TextBox control, the Text property is set directly. private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. //this.textBox1.InvokeRequired will be replaced by //this.InvokeRequired, if want to set many controls' //attribute or text. if (this.textBox1.InvokeRequired)// or this.InvokeRequired { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } }
3.BackgroundWorker-Komponente
Die bevorzugte Methode zur Implementierung von Multithreading in einer Anwendung ist die Verwendung der BackgroundWorker-Komponente. Die BackgroundWorker-Komponente verwendet ein ereignisgesteuertes Modell für die Multithread-Verarbeitung. Der Hintergrundthread führt Ihren DoWork-Ereignishandler aus, und der Thread, der Ihr Steuerelement erstellt hat, führt die Ereignishandler ProgressChanged und RunWorkerCompleted aus. Sie können Steuerelemente über die Ereignishandler ProgressChanged und RunWorkerCompleted aufrufen.
①Erstellen Sie eine Methode, um die Arbeit zu erledigen, die Sie in einem Hintergrundthread erledigen möchten. Rufen Sie in dieser Methode keine Steuerelemente auf, die aus dem Hauptthread erstellt wurden.
②Erstellen Sie eine Methode, um die Ergebnisse der Hintergrundarbeit zu melden, nachdem die Hintergrundarbeit beendet ist. In dieser Methode können vom Hauptthread erstellte Steuerelemente aufgerufen werden.
③ Binden Sie die in Schritt 1 erstellte Methode an das BackgroundWorker-Ereignis in der DoWork-Instanz und binden Sie die in Schritt 2 erstellte Methode an das RunWorkerCompleted-Ereignis derselben Instanz.
④ Um einen Hintergrundthread zu starten, rufen Sie die BackgroundWorker-Methode der RunWorkerAsync-Instanz auf.
Im folgenden Codebeispiel verwendet der DoWork-Ereignishandler Sleep, um Arbeit zu simulieren, die einige Zeit in Anspruch nimmt. Das TextBox-Steuerelement des Formulars wird nicht aufgerufen. Die Text-Eigenschaft des TextBox-Steuerelements wird direkt im RunWorkerCompleted-Ereignishandler festgelegt.
// This BackgroundWorker is used to demonstrate the // preferred way of performing asynchronous operations.private BackgroundWorker backgroundWorker1; // This event handler starts the form's // BackgroundWorker by calling RunWorkerAsync. // // The Text property of the TextBox control is set // when the BackgroundWorker raises the RunWorkerCompleted // event.private void setTextBackgroundWorkerBtn_Click( object sender, EventArgs e) { this.backgroundWorker1.RunWorkerAsync(); }// This event handler sets the Text property of the TextBox // control. It is called on the thread that created the // TextBox control, so the call is thread-safe. // // BackgroundWorker is the preferred way to perform asynchronous // operations.private void backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { this.textBox1.Text = "This text was set safely by BackgroundWorker."; }
Sie können den Fortschritt von Hintergrundaufgaben auch mithilfe des ProgressChanged-Ereignisses melden. Ein Beispiel, das dieses Ereignis enthält, finden Sie unter BackgroundWorker.
Das Obige ist der Inhalt des Selbststudiums C#02 von 0 – untergeordneter Thread, der auf die Steuerung des Hauptthreads (UI-Thread) zugreift. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www. php.cn)!