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.