Maison > Article > développement back-end > Auto-apprentissage C#02 à partir de 0 - le thread enfant accède au contrôle du thread principal (thread UI)
Si vous utilisez le multithreading pour améliorer les performances de votre application Windows Forms, vous devez vous assurer que les contrôles sont appelés de manière thread-safe.
L'accès aux contrôles Windows Forms n'est pas intrinsèquement thread-safe. Si deux threads ou plus manipulent l'état d'un contrôle, vous pouvez forcer le contrôle dans un état incohérent. D'autres bogues liés aux threads peuvent survenir, tels que des conditions de concurrence critique et des blocages. Assurez-vous d’accéder aux contrôles de manière thread-safe.
1. Problèmes souvent rencontrés par les débutants
Il est dangereux d'appeler un contrôle à partir d'un thread qui n'a jamais utilisé la méthode Invoke pour créer le contrôle. Vous trouverez ci-dessous un exemple d’appel non thread-safe. Un message InvalidOperationException sera émis pendant l'exécution, avec l'erreur « Le nom du contrôle de contrôle est accessible à partir d'un thread qui n'a pas créé le contrôle. »
// 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. Solution
Pour effectuer des appels thread-safe aux contrôles Windows Forms.
①Interrogez la propriété InvokeRequired du contrôle.
②Si InvokeRequired renvoie true, utilisez le délégué du contrôle appelant réel pour appeler Invoke.
③Si InvokeRequired renvoie false, veuillez appeler le contrôle directement.
Ici sont divisés en délégation d'exécution synchrone et délégation d'exécution asynchrone.
Dans l'exemple de code suivant, un appel thread-safe est implémenté dans la méthode ThreadProcSafe, qui est exécutée par un thread d'arrière-plan. Si InvokeRequired du contrôle TextBox renvoie true, la méthode ThreadProcSafe crée une instance SetTextCallback et la transmet à la méthode Invoke du formulaire. Cela provoque l'appel de la méthode SetText sur le thread qui a créé le contrôle TextBox et la définition de la propriété Text directement dans le contexte de ce thread.
// 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.Composant BackgroundWorker
La méthode privilégiée pour implémenter le multithreading dans une application est d'utiliser le composant BackgroundWorker. Le composant BackgroundWorker utilise un modèle basé sur les événements pour le traitement multithread. Le thread d'arrière-plan exécute votre gestionnaire d'événements DoWork et le thread qui a créé votre contrôle exécute les gestionnaires d'événements ProgressChanged et RunWorkerCompleted. Vous pouvez appeler des contrôles à partir des gestionnaires d'événements ProgressChanged et RunWorkerCompleted.
①Créez une méthode pour effectuer le travail que vous souhaitez faire dans un fil de discussion en arrière-plan. N'appelez pas les contrôles créés à partir du thread principal dans cette méthode.
②Créez une méthode pour rapporter les résultats du travail en arrière-plan une fois le travail en arrière-plan terminé. Les contrôles créés par le thread principal peuvent être appelés dans cette méthode.
③ Liez la méthode créée à l'étape 1 à l'événement BackgroundWorker dans l'instance DoWork et liez la méthode créée à l'étape 2 à l'événement RunWorkerCompleted de la même instance.
④ Pour démarrer un thread en arrière-plan, appelez la méthode BackgroundWorker de l'instance RunWorkerAsync.
Dans l'exemple de code suivant, le gestionnaire d'événements DoWork utilise Sleep pour simuler un travail qui prend un certain temps. Il n’appelle pas le contrôle TextBox du formulaire. La propriété Text du contrôle TextBox est définie directement dans le gestionnaire d'événements RunWorkerCompleted.
// 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."; }
Vous pouvez également signaler la progression des tâches en arrière-plan en utilisant l'événement ProgressChanged. Pour un exemple incluant cet événement, consultez BackgroundWorker.
Ce qui précède est le contenu de l'auto-apprentissage C#02 à partir de 0 - thread enfant accédant au contrôle du thread principal (thread UI). Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www. php.cn) !