The situation is as follows:

  • There is a window with a button1 button and a label1 label.
  • the button starts some kind of long operation, in a separate thread.
  • upon completion of the operation, output the result label1 .

When trying to change the value of label1.Text code falls with the exception InvalidOperationException :

WinForms:

This was not the case.

Invalid operation in multiple threads: attempting to access the 'label1' control element from a thread in which it was created.

WPF:

Because of a different thread of yours.

The calling thread cannot access this object, as the owner of this object is another thread.

Code example:

 private void button1_Click(object sender, EventArgs e) { (new Thread((s) => { var result = Worker.SomeLongOperation(); // следующая строчка падает c InvalidOperationException: this.label1.Text = result; })).Start(); } class Worker { public static string SomeLongOperation() { Thread.Sleep(1000); return "результат"; } } 
  • ... as part of the translation of canonical questions and answers from EnSO - PashaPash
  • And where is the link to the original question? If this is a translation, then it is not necessary to give several answers, it is enough to assemble one common one from the existing answers. - jfs
  • Answers from stackoverflow.com/questions/661561/… . The original question is not searched for in the text of the error, so I wrote the most general case possible. If you want to issue one answer - edit, general answers. I made out by analogy with ru.stackoverflow.com/questions/416584 and ru.stackoverflow.com/questions/417453 . - PashaPash
  • If the question is useful enough to translate it, then the translation should be done by the person who can choose the best. Mechanical translation, without understanding the meaning of the existing answers in English, is less useful. - jfs
  • @jfs, do you think that I am not competent enough and cannot "choose the best" between BeginInvoke and async? It did not occur to you that these are two independent solutions, each of which is applicable in a specific context (which depends on the version of the framework and on the scale of the problem). Here, on the contrary, one more detailed answer about background worker is missing. - PashaPash

2 answers 2

Solution for .NET 4.0 and later

Use Task Based Asynchronous Model (TAP) and async keywords — await :

 private async void button1_Click(object sender, EventArgs e) { string result = await Task.Factory.StartNew<string>( () => Worker.SomeLongOperation(), TaskCreationOptions.LongRunning); this.label1.Text = result; } 

Benefits:

  • The code is much shorter than other options, calls are recorded in the sequence in which they are executed.
  • No callbacks and manual work with threads.
  • async does not allow the event handler to complete, but it does not block the UI.
  • TaskCreationOptions.LongRunning prompts the scheduler that the operation will be really long and you should not use the thread pool to execute it.

Native support for async / await keywords appeared in .NET 4.5 and Visual Studio 2013.

This solution can also be used for .NET 4.0 and Silverlight 5 if you are using a version of Visual Studio no lower than 2012. To do this, you need to install the Microsoft.Bcl.Async package from NuGet.

Solution with progress display

If during execution you need to display the progress or intermediate results from the second thread, then you can use the Progress class:

 private async void button1_Click(object sender, EventArgs e) { var progress = new Progress<string>(s => label1.Text = s); string result = await Task.Factory.StartNew<string>( () => Worker.SomeLongOperation(progress), TaskCreationOptions.LongRunning); this.label1.Text = result; } class Worker { public static string SomeLongOperation(IProgress<string> progress) { // Perform a long running work... for (var i = 0; i < 10; i++) { Task.Delay(500).Wait(); progress.Report(i.ToString()); } return "результат"; } } 

Progress captures the SynchronizationContext at the time of creation, and uses it to perform operations, eliminating the manual invocation calls.

Solution for .NET 3.5 and earlier

Use Invoke / BeginInvoke :

 // WinForms: this.label1.BeginInvoke((MethodInvoker)(() => this.label1.Text = result)); // WPF: Dispatcher.BeginInvoke((Action)(() => this.label1.Content = result)); 

For .NET 2.0, which has not yet had lambdas, the equivalent code is written using anonymous delegates:

 // WinForms: this.label1.BeginInvoke((MethodInvoker)(delegate { this.label1.Text = result; }); // WPF: Dispatcher.BeginInvoke((Action)(delegate { this.label1.Content = result; }); 

Full code:

 private void button1_Click(object sender, EventArgs e) { (new Thread((s) => { var result = Worker.SomeLongOperation(); this.label1.BeginInvoke((MethodInvoker)(() => this.label1.Text = result)); })).Start(); } 

BeginInvoke will put the code for execution into the thread in which label1 was created and continue the execution of the background thread. When using Invoke instead of BeginInvoke background thread will be suspended until the execution of the code in the UI stream is completed.

  • 2
    Oh, that's the right answer. And why did you make it common? - VladD pm
  • @VladD is a translation of stackoverflow.com/a/18033198/1985167 . Apparently, the author decided not to earn a reputation on this :). - andreycha
  • @andreycha: Understand the hint. - VladD
  • 3
    @Stack is not "will", but "possible" - Pavel Mayorov
  • 2
    You can also add SynchronizationContext which can be used without UI if (SynchronizationContext.Current == null) SynchronizationContext.SetSynchronizationContext (new WindowsFormsSynchronizationContext ()); Sc = SynchronizationContext.Current; using Send or Post - Serginio

There is also an example for a UI to safely call from a non-main thread.

To interact with interface elements from another thread

  void SomeAsyncMethod() { // Do some work if (this.InvokeRequired) { this.Invoke((MethodInvoker)(() => { DoUpdateUI(); } )); } else { DoUpdateUI(); } } void DoUpdateUI() { // Your UI update code here } 

Here is this.Invoke control method

  • About Invoke said in the answer above :) Invoke itself does a check on this inside. InvokeRequired, so it is redundant in the calling code. - PashaPash
  • @PashaPash agrees, but as an option goes :) For a long time, for forgotten reasons, you had to use just such a construction. - DiMa Makarov