You are on page 1of 8

Cmo Realizar llamadas seguras para subprocesos en controles de formularios Windows Forms

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

Realizar llamadas seguras para subprocesos a controles de Windows Forms


Para realizar una llamada segura para subprocesos a un control de Windows Forms 1. Consulte la propiedad InvokeRequired del control. 2. Si la propiedad InvokeRequired devuelve true, llame a Invoke con un delegado que realice la
llamada real al control.

3. Si la propiedad InvokeRequired devuelve false, llame directamente al control.


En el siguiente ejemplo de cdigo, se implementa una llamada segura para subprocesos en el mtodo ThreadProcSafe, que el subproceso en segundo plano ejecuta. Si la propiedad InvokeRequired del controlTextBox devuelve true, el mtodo ThreadProcSafe crea una instancia de SetTextCallback y la pasa al mtodo Invoke del formulario. Esto hace que se llame al mtodo SetText en el subproceso que ha creado el control TextBox, y en este contexto de subproceso se establece directamente la propiedad Text. C# C++ VB // This event handler creates a thread that calls a // Windows Forms control in a thread-safe way.privatevoid 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.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; }

Realizar llamadas seguras para subprocesos mediante BackgroundWorker


La mejor manera de implementar el multithreading en la aplicacin es utilizar el componente BackgroundWorker. El componente BackgroundWorker utiliza un modelo orientado a eventos para el multithreading. El subproceso en segundo plano ejecuta el controlador de eventos DoWork y el subproceso que crea los controles llama a los controladores de eventos ProgressChanged y RunWorkerCompleted. Puede llamar a los controles desde los controladores de eventos RunWorkerCompleted y ProgressChanged.

Para realizar llamadas seguras para subprocesos mediante BackgroundWorker


1. Cree un mtodo para que las operaciones que desee realizar se lleven a cabo en el subproceso en segundo plano. No llame a los controles creados por el subproceso principal en este mtodo. 2. Cree un mtodo para notificar los resultados del trabajo en segundo plano una vez finalizado. Puede llamar a los controles creados por el subproceso principal en este mtodo. 3. Enlace el mtodo creado en el paso 1 al evento DoWork de una instancia de BackgroundWorker y enlace el mtodo creado en el paso 2 al evento RunWorkerCompleted de la misma instancia. 4. Para iniciar el subproceso en segundo plano, llame al mtodo RunWorkerAsync de la instancia de BackgroundWorker. En el siguiente ejemplo de cdigo, el controlador de eventos DoWork utiliza Sleep para simular trabajo que tarda algn tiempo. No llama al control TextBox del formulario. La propiedad Text del control TextBox se establece directamente en el controlador de eventos RunWorkerCompleted. C# C++ VB // This BackgroundWorker is used to demonstrate the // preferred way of performing asynchronous operations.private BackgroundWorker backgroundWorker1;

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

TOMADO: http://msdn.microsoft.com/es-es/library/ms171728.aspx DESDE GOOGLE.COM: control de llamadas con vb.net

You might also like