Escolar Documentos
Profissional Documentos
Cultura Documentos
Visual Studio 2010 Otras versiones Personas que lo han encontrado til: 0 de 1 - Valorar este tema Si utiliza multithreading para mejorar el rendimiento de las aplicaciones de Windows Forms, debe asegurarse de realizar llamadas a los controles de forma segura. El acceso a los controles de formularios Windows Forms no es inherentemente seguro para los subprocesos. Si dos o ms subprocesos manipulan el estado de un control, es posible obligar al control a pasar a un estado incoherente. Se pueden dar otros errores relacionados con los subprocesos, como condiciones de carrera e interbloqueos. Es importante asegurarse de que el acceso a los controles se realice de manera segura para los subprocesos. No es seguro llamar a un control desde un subproceso distinto del que cre el control sin utilizar el mtodo Invoke. A continuacin figura un ejemplo de una llamada que no es segura para los subprocesos. C# C++ VB // 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."; }
.NET Framework ayuda a detectar cundo el acceso a los controles se produce de una manera no segura para los subprocesos. Cuando se est ejecutando la aplicacin en un depurador y un subproceso distinto del que cre un control intenta llamar a ese control, el depurador inicia una excepcin InvalidOperationException con el mensaje: "Se tuvo acceso al control nombre del control desde un subproceso distinto a aquel en que lo cre". Esta excepcin aparece de forma fiable durante la depuracin y, en algunas circunstancias, en tiempo de ejecucin. Esta excepcin puede aparecer cuando se depuran aplicaciones escritas con una versin de .NET Framework anterior a .NET Framework versin 2.0. Cuando surja este problema, se recomienda corregirlo, si bien se puede deshabilitarlo estableciendo el valor de la propiedad CheckForIllegalCrossThreadCalls enfalse. Esto hace que el control se ejecute de la misma manera que si se ejecutara en Visual Studio .NET 2003 y .NET Framework 1.1.
Nota
Si usa controles ActiveX en un formulario, puede que se inicie la excepcin InvalidOperationException entre subproces controles ActiveX no admiten multithreading. Para obtener ms informacin sobre cmo utilizar los controles ActiveX aplicaciones no administradas. Si utiliza Visual Studio, puede evitar esta excepcin deshabilitando el proceso de hosped
// This method is executed on the worker thread and makes// a thread-safe call on the TextBox control.privatevoid ThreadProcSafe() { this.SetText("This text was set safely."); }
C# C++ VB // 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. privatevoid 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.if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, newobject[] { text }); } else { this.textBox1.Text = text; }
C# C++ VB // 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.privatevoid 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.privatevoid backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { this.textBox1.Text = "This text was set safely by BackgroundWorker."; }
Asimismo, se puede notificar el progreso de una tarea en segundo plano mediante el evento ProgressChanged. Para obtener un ejemplo que incluye ese evento, vea BackgroundWorker.
Ejemplo
El siguiente ejemplo de cdigo es una aplicacin de Windows Forms completa que se compone de un formulario con tres botones y un cuadro de texto. El primer botn muestra el acceso no seguro entre subprocesos, el segundo botn muestra el acceso seguro mediante Invoke y el tercer botn muestra el acceso seguro mediante BackgroundWorker.
Nota
Para obtener instrucciones sobre cmo ejecutar el ejemplo, vea Cmo: Compilar y ejecutar un ejemplo de cdigo comp los ensamblados System.Drawing y System.Windows.Forms.
C# C++ VB using System; using System.ComponentModel; using System.Threading; using System.Windows.Forms; namespace CrossThreadDemo { public class Form1 : Form { // This delegate enables asynchronous calls for setting // the text property on a TextBox control. delegate void SetTextCallback(string text); // This thread is used to demonstrate both thread-safe and // unsafe ways to call a Windows Forms control. private Thread demoThread = null; // This BackgroundWorker is used to demonstrate the // preferred way of performing asynchronous operations. private BackgroundWorker backgroundWorker1; private TextBox textBox1; private Button setTextUnsafeBtn; private Button setTextSafeBtn; private Button setTextBackgroundWorkerBtn; private System.ComponentModel.IContainer components = null; public Form1() { InitializeComponent(); } protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } // 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."; } // 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 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. if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } } // 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 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."; } #region Windows Form Designer generated code private void InitializeComponent() { this.textBox1 = new System.Windows.Forms.TextBox(); this.setTextUnsafeBtn = new System.Windows.Forms.Button(); this.setTextSafeBtn = new System.Windows.Forms.Button(); this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button(); this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); this.SuspendLayout(); // // textBox1 // this.textBox1.Location = new System.Drawing.Point(12, 12); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(240, 20); this.textBox1.TabIndex = 0; // // setTextUnsafeBtn // this.setTextUnsafeBtn.Location = new System.Drawing.Point(15, 55); this.setTextUnsafeBtn.Name = "setTextUnsafeBtn"; this.setTextUnsafeBtn.TabIndex = 1; this.setTextUnsafeBtn.Text = "Unsafe Call"; this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click); // // setTextSafeBtn // this.setTextSafeBtn.Location = new System.Drawing.Point(96, 55); this.setTextSafeBtn.Name = "setTextSafeBtn"; this.setTextSafeBtn.TabIndex = 2; this.setTextSafeBtn.Text = "Safe Call"; this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click); // // setTextBackgroundWorkerBtn // this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(177, 55); this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn"; this.setTextBackgroundWorkerBtn.TabIndex = 3; this.setTextBackgroundWorkerBtn.Text = "Safe BW Call"; this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click); this.backgroundWorker1.RunWorkerAsync();
// // backgroundWorker1 // this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerComple ted); // // Form1 // this.ClientSize = new System.Drawing.Size(268, 96); this.Controls.Add(this.setTextBackgroundWorkerBtn); this.Controls.Add(this.setTextSafeBtn); this.Controls.Add(this.setTextUnsafeBtn); this.Controls.Add(this.textBox1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); this.PerformLayout(); } #endregion [STAThread] static void Main() { Application.EnableVisualStyles(); Application.Run(new Form1()); } } }
Cuando se ejecuta la aplicacin y se hace clic en el botn Unsafe Call (Llamada no segura), aparece inmediatamente el texto "Written by the main thread" (Escrito por el subproceso principal) en el cuadro de texto.Dos segundos despus, cuando se intenta realizar la llamada no segura, el depurador de Visual Studio indica que se ha producido una excepcin. El depurador se detiene en la lnea del subproceso en segundo plano que intent escribir directamente en el cuadro de texto. Es preciso reiniciar la aplicacin para probar los otros dos botones. Cuando se hace clic en el botn Safe Call (Llamada segura), aparece el texto "Written by the main thread" (Escrito por el subproceso principal) en el cuadro de texto. Dos segundos despus, el cuadro de texto se establece en "Written by the background thread (Invoke)" (Escrito por el subproceso en segundo plano (Invoke)), lo que indica que se llam al mtodo Invoke. Cuando se hace clic en el botn Safe BW Call (Llamada segura a BW), aparece el texto "Written by the main thread" (Escrito por el subproceso principal) en el cuadro de texto. Dos segundos despus, el cuadro de texto se establece en "Written by the main thread after the background thread completed" (Escrito por el subproceso principal una vez completado el subproceso en segundo plano), lo que indica que se llam al controlador del evento RunWorkerCompleted de BackgroundWorker.
Programacin eficaz
Precaucin
Cuando se utiliza el multithreading de cualquier ordenacin, el cdigo se puede exponer a errores graves y complejos. P subprocesamiento administrado antes de implementar cualquier solucin que utilice multithreading.
Vea tambin
Tareas Cmo: Ejecutar una operacin en segundo plano Cmo: Implementar un formulario que utiliza una operacin en segundo plano