¿Cómo actualizo la GUI de otro hilo?

¿Cuál es la forma más sencilla de actualizar una Label desde otro hilo?

Tengo un Form en thread1 , y de eso estoy comenzando otro hilo ( thread2 ). Mientras thread2 está procesando algunos archivos, me gustaría actualizar una Label en el Form con el estado actual del thread2 de thread2 .

¿Cómo puedo hacer eso?

Para .NET 2.0, aquí hay un buen código que escribí que hace exactamente lo que quiere, y funciona para cualquier propiedad en un 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 }); } } 

Llámalo así:

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

Si usa .NET 3.0 o superior, puede volver a escribir el método anterior como método de extensión de la clase Control , lo que simplificaría la llamada a:

 myLabel.SetPropertyThreadSafe("Text", status); 

ACTUALIZACIÓN 05/10/2010:

Para .NET 3.0 debe usar este código:

 private delegate void SetPropertyThreadSafeDelegate( Control @this, Expression> property, TResult value); public static void SetPropertyThreadSafe( this Control @this, Expression> 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 (SetPropertyThreadSafe), new object[] { @this, property, value }); } else { @this.GetType().InvokeMember( propertyInfo.Name, BindingFlags.SetProperty, null, @this, new object[] { value }); } } 

que usa expresiones LINQ y lambda para permitir una syntax mucho más limpia, simple y segura:

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

No solo se verifica el nombre de la propiedad en tiempo de comstackción, sino que también el tipo de propiedad, por lo que es imposible (por ejemplo) asignar un valor de cadena a una propiedad booleana y, por lo tanto, provocar una excepción de tiempo de ejecución.

Desafortunadamente, esto no impide que nadie haga cosas estúpidas, como pasar la propiedad y el valor de otro Control , por lo que lo siguiente será una comstackción feliz:

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

Por lo tanto, agregué las comprobaciones de tiempo de ejecución para asegurarme de que la propiedad transferida realmente pertenece al Control que se llama el método. No es perfecto, pero es mucho mejor que la versión .NET 2.0.

Si alguien tiene más sugerencias sobre cómo mejorar este código para la seguridad en tiempo de comstackción, ¡por favor comente!

La forma más sencilla es pasar un método anónimo a 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 

Tenga en cuenta que Invoke bloquea la ejecución hasta que se completa; este es el código síncrono. La pregunta no pregunta sobre el código asíncrono, pero hay un montón de contenido en Stack Overflow sobre cómo escribir código asíncrono cuando desea obtener más información al respecto.

Manejando un largo trabajo

Desde .NET 4.5 y C # 5.0 , debe utilizar Patrón asincrónico basado en tareas (TAP) junto con la función de sincronización : aguarde las palabras clave en todas las áreas (incluida la GUI):

TAP es el patrón de diseño asíncrono recomendado para el nuevo desarrollo

en lugar del Modelo de progtwigción asíncrono (APM) y el Patrón asincrónico basado en eventos (EAP) (este último incluye la clase BackgroundWorker ).

Entonces, la solución recomendada para un nuevo desarrollo es:

  1. Implementación asíncrona de un controlador de eventos (Sí, eso es todo):

     private async void Button_Clicked(object sender, EventArgs e) { var progress = new Progress(s => label.Text = s); await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress), TaskCreationOptions.LongRunning); label.Text = "completed"; } 
  2. Implementación del segundo hilo que notifica el hilo de UI:

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

Observe lo siguiente:

  1. Código breve y limpio escrito de forma secuencial sin devoluciones de llamadas e hilos explícitos.
  2. Tarea en lugar de hilo .
  3. async keyword, que permite usar await, lo que a su vez impide que el controlador de eventos scope el estado de finalización hasta que la tarea finalice y, mientras tanto, no bloquea el hilo de la interfaz de usuario.
  4. Clase de progreso (ver Interfaz de IProgress ) que admite el principio de diseño de Separación de preocupaciones (SoC) y no requiere despachador e invocación explícitos. Utiliza el SynchronizationContext actual desde su lugar de creación (aquí el hilo de UI).
  5. TaskCreationOptions.LongRunning que sugiere no poner la tarea en cola en ThreadPool .

Para ver ejemplos más detallados, vea: El futuro de C #: las cosas buenas llegan a aquellos que 'aguardan' por Joseph Albahari .

Ver también sobre el concepto de UI Threading Model .

Manejo de excepciones

El siguiente fragmento es un ejemplo de cómo manejar las excepciones y activar la propiedad Enabled del botón para evitar clics múltiples durante la ejecución del fondo.

 private async void Button_Click(object sender, EventArgs e) { button.Enabled = false; try { var progress = new Progress(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 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..."); } } 

Variación de la solución más simple de Marc Gravell para .NET 4:

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

O use el delegado de Acción en su lugar:

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

Vea aquí para una comparación de los dos: MethodInvoker vs Action for Control.BeginInvoke

Método de extensión Fire and forget para .NET 3.5+

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

Esto se puede llamar usando la siguiente línea de código:

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

Esta es la forma clásica en que debes hacer esto:

 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(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 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; } } } 

Su hilo de trabajo tiene un evento. Su subproceso de interfaz de usuario comienza otro subproceso para hacer el trabajo y conecta ese evento de trabajador para que pueda mostrar el estado del subproceso de trabajo.

Luego, en la interfaz de usuario, debe cruzar los hilos para cambiar el control real … como una etiqueta o una barra de progreso.

La solución simple es usar Control.Invoke .

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

El código de enhebrado a menudo tiene errores y es siempre difícil de probar. No es necesario que escriba el código de enhebrado para actualizar la interfaz de usuario de una tarea en segundo plano. Simplemente use la clase BackgroundWorker para ejecutar la tarea y su método ReportProgress para actualizar la interfaz de usuario. Normalmente, solo informa un porcentaje completado, pero hay otra sobrecarga que incluye un objeto de estado. Aquí hay un ejemplo que solo informa un objeto de cadena:

  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(); } 

Eso está bien si siempre quieres actualizar el mismo campo. Si tiene que hacer actualizaciones más complicadas, puede definir una clase para representar el estado de la IU y pasarla al método ReportProgress.

Una última cosa, asegúrese de configurar el indicador WorkerReportsProgress , o el método ReportProgress se ignorará por completo.

La gran mayoría de las respuestas utilizan Control.Invoke que es una condición de carrera que está por ocurrir . Por ejemplo, considere la respuesta aceptada:

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

Si el usuario cierra el formulario justo antes de this.Invoke Se this.Invoke (recuerde que this es el objeto Form ), es probable que se ObjectDisposedException una ObjectDisposedException .

La solución es usar SynchronizationContext , específicamente SynchronizationContext.Current como hamilton.danielb sugiere (otras respuestas se basan en implementaciones específicas de SynchronizationContext , que son completamente innecesarias). Yo modificaría ligeramente su código para usar SynchronizationContext.Post lugar de SynchronizationContext.Send though (ya que normalmente no hay necesidad de que el subproceso de trabajo espere):

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

Tenga en cuenta que en .NET 4.0 y versiones superiores, realmente debería utilizar tareas para operaciones asíncronas. Vea la respuesta de n-san para el enfoque equivalente basado en tareas (usando TaskScheduler.FromCurrentSynchronizationContext ).

Finalmente, en .NET 4.5 y versiones posteriores también puede usar Progress (que básicamente captura SynchronizationContext.Current al momento de su creación) como lo demostró Ryszard Dżegan’s en los casos en que la operación de larga duración necesita ejecutar el código de UI mientras sigue funcionando.

Deberá asegurarse de que la actualización se realice en el hilo correcto; el hilo de la interfaz de usuario

Para hacer esto, deberá invocar el controlador de eventos en lugar de llamarlo directamente.

Puedes hacer esto al plantear tu evento de esta manera:

(El código está tipeado aquí fuera de mi cabeza, así que no he verificado la syntax correcta, etc., pero debería hacerlo funcionar).

 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 ( ... ); } } } 

Tenga en cuenta que el código anterior no funcionará en proyectos WPF, ya que los controles WPF no implementan la interfaz ISynchronizeInvoke .

Para asegurarse de que el código anterior funciona con Windows Forms y WPF, y todas las demás plataformas, puede echar un vistazo a las AsyncOperation , AsyncOperationManager y SynchronizationContext .

Para poder crear eventos de esta manera, he creado un método de extensión, que me permite simplificar la creación de un evento simplemente llamando:

 MyEvent.Raise(this, EventArgs.Empty); 

Por supuesto, también puede hacer uso de la clase BackGroundWorker, que resumirá este asunto por usted.

Deberá invocar el método en el hilo de la GUI. Puedes hacerlo llamando Control.Invoke.

Por ejemplo:

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

Debido a la trivialidad del escenario, en realidad tendré la encuesta de hilos de UI para el estado. Creo que encontrarás que puede ser bastante elegante.

 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..."; } } } 

El enfoque evita la operación de ISynchronizeInvoke.Invoke requerido cuando se utilizan los métodos ISynchronizeInvoke.Invoke e ISynchronizeInvoke.BeginInvoke . No tiene nada de malo utilizar la técnica de cálculo de referencias, pero hay algunas advertencias que debe tener en cuenta.

  • Asegúrese de no llamar a BeginInvoke demasiada frecuencia o podría invadir la bomba de mensajes.
  • Llamar a Invoke en el hilo de trabajo es una llamada de locking. Paralizará temporalmente el trabajo que se está realizando en ese hilo.

La estrategia que propongo en esta respuesta invierte los roles de comunicación de los hilos. En lugar de que el hilo del trabajador empuje los datos, el hilo de la interfaz de usuario lo sondea. Este es un patrón común utilizado en muchos escenarios. Como todo lo que quiere hacer es mostrar información de progreso del hilo de trabajo, entonces creo que encontrará que esta solución es una gran alternativa a la solución de cálculo de referencias. Tiene las siguientes ventajas.

  • La interfaz de usuario y los hilos de trabajo permanecen ligeramente acoplados en comparación con el enfoque Control.Invoke o Control.BeginInvoke , que los acopla estrechamente.
  • El subproceso de interfaz de usuario no impedirá el progreso del subproceso de trabajo.
  • El subproceso trabajador no puede dominar el tiempo que el subproceso de la interfaz de usuario pasa la actualización.
  • Los intervalos a los que la UI y los subprocesos de trabajo realizan operaciones pueden permanecer independientes.
  • El subproceso de trabajo no puede invadir la bomba de mensajes del subproceso de UI.
  • El subproceso de interfaz de usuario determina cuándo y con qué frecuencia se actualiza la interfaz de usuario.

Ninguna de las cosas de Invocar en las respuestas anteriores es necesaria.

Necesitas mirar 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 } 

Para muchos propósitos, es tan simple como esto:

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

“serviceGUI ()” es un método de nivel GUI dentro del formulario (esto) que puede cambiar tantos controles como desee. Llame a “updateGUI ()” del otro hilo. Se pueden agregar parámetros para pasar valores, o (probablemente más rápido) usar variables de ámbito de clase con lockings si es necesario si existe la posibilidad de un conflicto entre los subprocesos que acceden a ellos que podrían causar inestabilidad. Use BeginInvoke en lugar de Invoke si el hilo que no es GUI es de tiempo crítico (manteniendo la advertencia de Brian Gideon en mente).

Esto en mi variación C # 3.0 de la solución de Ian Kemp:

 public static void SetPropertyInGuiThread(this C control, Expression> 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>, V>)SetPropertyInGuiThread, new object[] { control, property, value } ); else propertyInfo.SetValue(control, value, null); } 

Lo llamas así:

 myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!") 
  1. Agrega null-checking al resultado de “como MemberExpression”.
  2. Mejora la seguridad de tipo estático.

De lo contrario, el original es una solución muy buena.

Este es similar a la solución anterior que usa .NET Framework 3.0, pero resolvió el problema del soporte de seguridad en tiempo de comstackción .

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

Usar:

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

El comstackdor fallará si el usuario pasa el tipo de datos incorrecto.

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

Salvete! Después de buscar esta pregunta, encontré que las respuestas de FrankG y Oregon Ghost eran las más fáciles que me resultaban más útiles. Ahora, codigo en Visual Basic y ejecuté este fragmento a través de un convertidor; así que no estoy seguro cómo resulta.

Tengo un formulario de diálogo llamado form_Diagnostics, que tiene un cuadro de texto enriquecido, llamado updateDiagWindow, que estoy usando como una especie de pantalla de registro. Necesitaba poder actualizar su texto de todos los hilos. Las líneas adicionales permiten que la ventana se desplace automáticamente a las líneas más nuevas.

Y entonces, ahora puedo actualizar la pantalla con una línea, desde cualquier lugar del progtwig completo de la manera que crees que funcionaría sin ningún tipo de subprocesamiento:

  form_Diagnostics.updateDiagWindow(whatmessage); 

Código principal (ponga esto dentro del código de clase de su formulario):

 #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 
 Label lblText; //initialized elsewhere void AssignLabel(string text) { if (InvokeRequired) { BeginInvoke((Action)AssignLabel, text); return; } lblText.Text = text; } 

Tenga en cuenta que se prefiere BeginInvoke() sobre Invoke() porque es menos probable que cause deadlocks (sin embargo, esto no es un problema aquí al asignar texto a una etiqueta):

Al usar Invoke() está esperando que el método regrese. Ahora bien, es posible que haga algo en el código invocado que tendrá que esperar al hilo, que puede no ser inmediatamente obvio si está oculto en algunas funciones a las que está llamando, lo que puede ocurrir indirectamente a través de controladores de eventos. Entonces, estarías esperando el hilo, el hilo te estaría esperando y estás en punto muerto.

En realidad, esto causó la suspensión de algunos de nuestros software lanzados. Fue fácil repararlo reemplazando Invoke() con BeginInvoke() . A menos que necesite una operación sincrónica, que puede ser el caso si necesita un valor de retorno, use BeginInvoke() .

Cuando me encontré con el mismo problema, busqué ayuda de Google, pero en lugar de darme una solución simple, me confundió más al dar ejemplos de MethodInvoker y bla, bla, bla. Así que decidí resolverlo solo. Aquí está mi solución:

Haz un delegado como este:

 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 } 

Puede llamar a esta función en un nuevo hilo como este

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

No se confunda con Thread(() => .....) . Uso una función anónima o una expresión lambda cuando trabajo en un hilo. 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)); } } 

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(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”.

You must use invoke and delegate

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

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); } } 

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); 

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.

When you’re in the UI thread you could ask it for its synchronization context task scheduler. It would give you a TaskScheduler that schedules everything on the UI thread.

Then you can chain your tasks so that when the result is ready then another task (which is scheduled on the UI thread) picks it and assigns it to a label.

 public partial class MyForm : Form { private readonly TaskScheduler _uiTaskScheduler; public MyForm() { InitializeComponent(); _uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } private void buttonRunAsyncOperation_Click(object sender, EventArgs e) { RunAsyncOperation(); } private void RunAsyncOperation() { var task = new Task(LengthyComputation); task.ContinueWith(antecedent => UpdateResultLabel(antecedent.Result), _uiTaskScheduler); task.Start(); } private string LengthyComputation() { Thread.Sleep(3000); return "47"; } private void UpdateResultLabel(string text) { labelResult.Text = text; } } 

This works for tasks (not threads) which are the preferred way of writing concurrent code now .

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 .