How to execute task in the wpf background while able to provide report and allow cancellation?
I thought I answered your question here. If you need more sample code on how to do this using Task Parallel Library, with CancellationTokenSource
and IProgress<T>
, here it is:
Action _cancelWork;
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
this.StartButton.IsEnabled = false;
this.StopButton.IsEnabled = true;
try
{
var cancellationTokenSource = new CancellationTokenSource();
this._cancelWork = () =>
{
this.StopButton.IsEnabled = false;
cancellationTokenSource.Cancel();
};
var limit = 10;
var progressReport = new Progress<int>((i) =>
this.TextBox.Text = (100 * i / (limit-1)).ToString() + "%");
var token = cancellationTokenSource.Token;
await Task.Run(() =>
DoWork(limit, token, progressReport),
token);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
this.StartButton.IsEnabled = true;
this.StopButton.IsEnabled = false;
this._cancelWork = null;
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
this._cancelWork?.Invoke();
}
private int DoWork(
int limit,
CancellationToken token,
IProgress<int> progressReport)
{
var progress = 0;
for (int i = 0; i < limit; i++)
{
progressReport.Report(progress++);
Thread.Sleep(2000); // simulate a work item
token.ThrowIfCancellationRequested();
}
return limit;
}
Execute task in background in WPF application
With .NET 4.5 (or .NET 4.0 + Microsoft.Bcl.Async), the best way is to use Task
-based API and async/await
. It allows to use the convenient (pseudo-)sequential code workflow and have structured exception handling.
Example:
private async void Start(object sender, RoutedEventArgs e)
{
try
{
await Task.Run(() =>
{
int progress = 0;
for (; ; )
{
System.Threading.Thread.Sleep(1);
progress++;
Logger.Info(progress);
}
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
More reading:
How to execute task in the WPF background while able to provide report and allow cancellation?
Async in 4.5: Enabling Progress and Cancellation in Async APIs.
Async and Await.
Async/Await FAQ.
C# Threading/Async: Running a task in the background while UI is interactable
You've definitely implemented it incorrectly. You're returning a Task<int>
, but only once all the work has already been done.
It seems to me that you should probably just have a synchronous method:
private static void MyFunction()
{
// Loop in here
}
Then start a task for it like this:
Task task = Task.Run((Action) MyFunction);
You can then await that task if you want - although in the example you've given, there's no point in doing so, as you're not doing anything after the await
anyway.
I'd also agree with Reed that using a CancellationToken
would be cleaner than a static flag somewhere else.
How to run and interact with an async Task from a WPF gui
Long story short:
private async void ButtonClickAsync(object sender, RoutedEventArgs e)
{
// modify UI object in UI thread
txt.Text = "started";
// run a method in another thread
await HeavyMethodAsync(txt);
// <<method execution is finished here>>
// modify UI object in UI thread
txt.Text = "done";
}
// This is a thread-safe method. You can run it in any thread
internal async Task HeavyMethodAsync(TextBox textBox)
{
while (stillWorking)
{
textBox.Dispatcher.Invoke(() =>
{
// UI operation goes inside of Invoke
textBox.Text += ".";
// Note that:
// Dispatcher.Invoke() blocks the UI thread anyway
// but without it you can't modify UI objects from another thread
});
// CPU-bound or I/O-bound operation goes outside of Invoke
// await won't block UI thread, unless it's run in a synchronous context
await Task.Delay(51);
}
}
Result:
started....................done
You need to know about (1) how to write async
code (2) how to run UI operations in another thread and (3) how to cancel a task.
I'm not getting into (3) cancellation mechanism in this post. Just know that you can create a CancellationTokenSource
, which gives you a CancellationToken
which you can pass into any method. You cancel the source, all tokens will know.
async
and await
:
Basics of async
and await
You can only
await
in anasync
method.You can only
await
an awaitable object (i.e.Task
,ValueTask
,Task<T>
,IAsyncEnumerable<T>
, etc.) These objects wrap around the return type of anasync
method andawait
keyword unwraps them. (see Wrapping and Unwrapping section)Asynchronous method names should always end with
Async
to increase readability and to prevent mistakes.// Synchronous method:
TResult MethodName(params) { }
// Asynchronous method:
async Task<TResult> MethodNameAsync(params) { }
The magic of async
and await
The
async-await
syntactic feature, uses a state-machine to let the compiler give up and take back the control over theawaited Task
in anasync
method.The execution waits at
await
for the task to finish and returns back its results, without blocking the main thread.Task.Run
queues aTask
in the thread pool. (Unless the it's a pure operation.)
i.e. Theasync
method does not run in another thread.async
andawait
by themselves don't have anything to do with thread creation.
So
When you run a Task
(e.g. Task.Run(action)
) you (re)use a thread for that action. And you can put that task in an async
method to control its flow. By putting async
in the method signature you tell the compiler to use state-machine to control the flow of that method (this does not mean threading at all). And by await
ing the task you prevent the execution flow within that method from moving past the await
ed statement without blocking UI thread. If you want to pass the flow onto the caller then the async
method itself can become a Task
so you'll be able to cascade the same pattern out into the caller and so forth:
async Task Caller() { await Method(); }
async Task Method() { await Inner(); }
async Task Inner() { await Task.Run(action); }
The event handler looks like the code below.
Two possible cases for presense of async in the signature of ExecuteLongProcedure
(case 1 and 2) and MyButton_ClickAsync
(case A and B) are explained:
private async void MyButton_ClickAsync(object sender, RoutedEventArgs e)
{
//queue a task to run on threadpool
// 1. if ExecuteLongProcedure is a normal method and returns void
Task task = Task.Run(()=>
ExecuteLongProcedure(this, intParam1, intParam2, intParam3)
);
// or
// 2. if ExecuteLongProcedure is an async method and returns Task
Task task = ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);
// either way ExecuteLongProcedure is running asynchronously here
// the method will exit if you don't wait for the Task to finish
// A. wait without blocking the main thread
// -> requires MyButton_ClickAsync to be async
await task;
// or
// B. wait and block the thread (NOT RECOMMENDED AT ALL)
// -> does not require MyButton_ClickAsync to be async
task.Wait();
}
Async method return types:
Suppose you have the following declaration:
private async ReturnType MethodAsync() { ... }
If
ReturnType
isTask
thenawait MethodAsync();
returnsvoid
If
ReturnType
isTask<T>
thenawait MethodAsync();
returns a value of typeT
This is called Unwrapping, see the next section (Wrapping and Unrwapping).
If
ReturnType
isvoid
you can'tawait
it- If you try writing
await MethodAsync();
, you will get a compile error saying:
cannot await void
- You can only fire and forget i.e. just call the method normally:
MethodAsync();
and then go on with your life. - The
MethodAsync
execution will be synchronous, however since it hasasync
it will allow you to take advantage of the magic, i.e. you can writeawait task
within the method to control the flow of execution. - This is how WPF handles your button click event handler, obviously because your event handler returns
void
.
- If you try writing
The return type of an async method must be
void
,Task
,Task<T>
, a task-like type,IAsyncEnumerable<T>
, orIAsyncEnumerator<T>
Wrapping and Unrwapping:
Wrapping:
async
methods wrap their return values in a Task
.
E.g., this method wraps a Task
around an int
and returns it:
// async Task<int>
private async Task<int> GetOneAsync()
{
int val = await CalculateStuffAsync();
return val;
// returns an integer
}
Unwrapping:
To retrieve or unwrap the value which is wrapped inside a Task<>
:
- asynchronous option:
await
- synchronous option:
task.Result
ortask.GetAwaiter().GetResult()
ortask.WaitAndUnwrapException()
or read How to call asynchronous method from synchronous method in C#?
e.g. await
unwraps the int
out of the Task
:
Task<int> task = GetOneAsync();
int number = await task;
//int <- Task<int>
Different ways to wrap and unwrap:
private Task<int> GetNumber()
{
Task<int> task;
task = Task.FromResult(1); // the correct way to wrap a quasi-atomic operation, the method GetNumber is not async
task = Task.Run(() => 1); // not the best way to wrap a number
return task;
}
private async Task<int> GetNumberAsync()
{
int number = await Task.Run(GetNumber); // unwrap int from Task<int>
// bad practices:
// int number = Task.Run(GetNumber).GetAwaiter().GetResult(); // sync over async
// int number = Task.Run(GetNumber).Result; // sync over async
// int number = Task.Run(GetNumber).Wait(); // sync over async
return number; // wrap int in Task<int>
}
Still confused? Read async return types on MSDN.
To unwrap a task result, Always try to use
await
instead of.Result
otherwise there will be no asynchronous benefit but only asynchronous disadvantages. The latter is called "sync over async".
Note:
await
is a asynchronous and is different from task.Wait()
which is synchronous. But they both do the same thing which is waiting for the task to finish.
await
is a asynchronous and is different from task.Result
which is synchronous. But they both do the same thing which is waiting for the task to finish and unwrapping and returning back the results.
To have a wrapped value, you can always use Task.FromResult(1)
instead of creating a new thread by using Task.Run(() => 1)
.
Task.Run
is newer (.NetFX4.5) and simpler version of Task.Factory.StartNew
WPF GUI:
This is where I explain how to run UI operations in another thread.
Blocking:
First thing you need to know about WPF async event handlers is that the Dispatcher
will provide a synchronization context. Explained here
CPU-bound or IO-bound operations such as Sleep
and task.Wait()
will block and consume the thread even if they are called in a method with async
keyword. but await Task.Delay()
tells the state-machine to stop the flow of execution on the thread so it does not consume it; meaning that the thread resources can be used elsewhere:
private async void Button_Click(object sender, RoutedEventArgs e)
{
Thread.Sleep(1000);//stops, blocks and consumes threadpool resources
await Task.Delay(1000);//stops without consuming threadpool resources
Task.Run(() => Thread.Sleep(1000));//does not stop but consumes threadpool resources
await Task.Run(() => Thread.Sleep(1000));//literally the WORST thing to do
}
Thread Safety:
If you have to access GUI asynchronously (inside ExecuteLongProcedure
method), invoke any operation which involves modification to any non-thread-safe object. For instance, any WPF GUI object must be invoked using a Dispatcher
object which is associated with the GUI thread:
void UpdateWindow(string text)
{
//safe call
Dispatcher.Invoke(() =>
{
txt.Text += text;
});
}
However, If a task is started as a result of a property changed callback from the ViewModel, there is no need to use Dispatcher.Invoke
because the callback is actually executed from the UI thread.
Accessing collections on non-UI Threads
WPF enables you to access and modify data collections on threads other than the one that created the collection. This enables you to use a background thread to receive data from an external source, such as a database, and display the data on the UI thread. By using another thread to modify the collection, your user interface remains responsive to user interaction.
Value changes fired by INotifyPropertyChanged are automatically marshalled back onto the dispatcher.
How to enable cross-thread access
Remember, async
method itself runs on the main thread. So this is valid:
private async void MyButton_ClickAsync(object sender, RoutedEventArgs e)
{
txt.Text = "starting"; // UI Thread
await Task.Run(()=> ExecuteLongProcedure1());
txt.Text = "waiting"; // UI Thread
await Task.Run(()=> ExecuteLongProcedure2());
txt.Text = "finished"; // UI Thread
}
Another way to invoke UI operations from UI thread is to use SynchronizationContext
as described here. SynchronizationContext
is a stronger abstraction than Dispatcher
and it's cross-platform.
var uiContext = SynchronizationContext.Current;
while (stillWorking)
{
uiContext.Post(o =>
{
textBox.Text += ".";
}, null);
await Task.Delay(51);
}
Patterns:
Fire and forget pattern:
For obvious reasons this is how your WPF GUI event handlers such as Button_ClickAsync
are called.
void Do()
{
// CPU-Bound or IO-Bound operations
}
async void DoAsync() // returns void
{
await Task.Run(Do);
}
void FireAndForget() // not blocks, not waits
{
DoAsync();
}
Fire and observe:
Task-returning methods are better since unhandled exceptions trigger the TaskScheduler.UnobservedTaskException
.
void Do()
{
// CPU-Bound or IO-Bound operations
}
async Task DoAsync() // returns Task
{
await Task.Run(Do);
}
void FireAndWait() // not blocks, not waits
{
Task.Run(DoAsync);
}
Fire and wait synchronously while wasting thread resources:
This is known as Sync over async, it is a synchronous operation but it uses more than one thread which may cause starvation. This happens when you call Wait()
or try to read results directly from task.Result
before the task is finished.
(AVOID THIS PATTERN)
void Do()
{
// CPU-Bound or IO-Bound operations
}
async Task DoAsync() // returns Task
{
await Task.Run(Do);
}
void FireAndWait() // blocks, waits and uses 2 more threads. Yikes!
{
var task = Task.Run(DoAsync);
task.Wait();
}
Is that all to it?
No. There is a lot more to learn about async
, its context and its continuation. This blogpost is especially recommended.
Task uses Thread? Are you sure?
Not necessarily. Read this answer to know more about the true face of async
.
Stephen Cleary has explained async-await
perfectly. He also explains in his other blog post when there is no thread involved.
Read more
ValueTask and Task
MSDN explains Task
MSDN explains async
how-to-call-asynchronous-method-from-synchronous-method
async await
- Behind the scenes
async await
- FAQ
Make sure you know the difference between Asynchronous, Parallel and Concurrent.
You may also read a simple asynchronous file writer to know where you should concurrent.
Investigate concurrent namespace
Ultimately, read this e-book: Patterns_of_Parallel_Programming_CSharp
How to use CancellationTokenSource to close a dialog on another thread?
Your problem is that your preview window runs on another thread. When you trigger cancellation, you execute the registered action of the cancellation token on that thread, not on the thread your preview is running on.
The gold standard in these cases is to not use two UI threads. This will usually cause trouble and the work you need to handle them is usually not worth it.
If you want to stay with your solution or if you want to trigger cancellation from a background thread, you have to marshal your close operation to the thread your window is opened in:
Action closeAction = () => previewWindow.Close();
previewWindow.Dispatcher.Invoke(closeAction);
Raise an event with the most recent data after some time
I would do it the other way around, i.e., run the UI update task on the UI thread, and request the data from there. In a nutshell:
async Task UpdateUIAsync(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Dispatcher.Yield(DispatcherPriority.Background);
var data = await GetDataAsync(token);
// do the UI update (or ViewModel update)
this.TextBlock.Text = "data " + data;
}
}
async Task<int> GetDataAsync(CancellationToken token)
{
// simulate async data arrival
await Task.Delay(10, token).ConfigureAwait(false);
return new Random(Environment.TickCount).Next(1, 100);
}
This updates the status as fast as data arrives, but note await Dispatcher.Yield(DispatcherPriority.Background)
. It's there to keeps the UI responsive if data is arriving too fast, by giving the status update iterations a lower priority than user input events.
[UPDATE] I decided to take this a bit further and show how to handle the case when there's a background operation constantly producing the data. We might use Progress<T>
pattern to post updates to the UI thread (as shown here). The problem with this would be that Progress<T>
uses SynchronizationContext.Post
which queues callbacks asynchronously. Thus, the currently shown data item might not have been the most recent one already when it got displayed.
To avoid that, I created Buffer<T>
class, which is essentially a producer/consumer for a single data item. It exposes async Task<T> GetData()
on the consumer side. I couldn't find anything similar in System.Collections.Concurrent
, although it may already exist somewhere (I'd be interested if someone points that out). Below is a complete WPF app:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace Wpf_21626242
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Content = new TextBox();
this.Loaded += MainWindow_Loaded;
}
async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
try
{
// cancel in 10s
var cts = new CancellationTokenSource(10000);
var token = cts.Token;
var buffer = new Buffer<int>();
// background worker task
var workerTask = Task.Run(() =>
{
var start = Environment.TickCount;
while (true)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(50);
buffer.PutData(Environment.TickCount - start);
}
});
// the UI thread task
while (true)
{
// yield to keep the UI responsive
await Dispatcher.Yield(DispatcherPriority.Background);
// get the current data item
var result = await buffer.GetData(token);
// update the UI (or ViewModel)
((TextBox)this.Content).Text = result.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/// <summary>Consumer/producer async buffer for single data item</summary>
public class Buffer<T>
{
volatile TaskCompletionSource<T> _tcs = new TaskCompletionSource<T>();
object _lock = new Object(); // protect _tcs
// consumer
public async Task<T> GetData(CancellationToken token)
{
Task<T> task = null;
lock (_lock)
task = _tcs.Task;
try
{
// observe cancellation
var cancellationTcs = new TaskCompletionSource<bool>();
using (token.Register(() => cancellationTcs.SetCanceled(),
useSynchronizationContext: false))
{
await Task.WhenAny(task, cancellationTcs.Task).ConfigureAwait(false);
}
token.ThrowIfCancellationRequested();
// return the data item
return await task.ConfigureAwait(false);
}
finally
{
// get ready for the next data item
lock (_lock)
if (_tcs.Task == task && task.IsCompleted)
_tcs = new TaskCompletionSource<T>();
}
}
// producer
public void PutData(T data)
{
TaskCompletionSource<T> tcs;
lock (_lock)
{
if (_tcs.Task.IsCompleted)
_tcs = new TaskCompletionSource<T>();
tcs = _tcs;
}
tcs.SetResult(data);
}
}
}
}
Related Topics
What Is the Maximum Resolution of C# .Net Bitmap
Checking User Name or User Email Already Exists
What's the Difference Between Returning Void and Returning a Task
How to Write Asynchronous Linq Query
Check If Value Exists in Datatable
How to Initialize a List<T> to a Given Size (As Opposed to Capacity)
Print Existing PDF (Or Other Files) in C#
How to Get a Uri of the Image Stored in the Resources
Shellexecute Equivalent in .Net
Why Use Try {} Finally {} with an Empty Try Block
Convert Bitmaps to One Multipage Tiff Image in .Net 2.0
Convert String to Hex-String in C#
What's the Difference Between the Webconfigurationmanager and the Configurationmanager