C# async/await Progress event on Task object
Simply put, Task
doesn't support progress. However, there's already a conventional way of doing this, using the IProgress<T>
interface. The Task-based Asynchronous Pattern basically suggests overloading your async methods (where it makes sense) to allow clients to pass in an IProgress<T>
implementation. Your async method would then report progress via that.
The Windows Runtime (WinRT) API does have progress indicators built-in, in the IAsyncOperationWithProgress<TResult, TProgress>
and IAsyncActionWithProgress<TProgress>
types... so if you're actually writing for WinRT, those are worth looking into - but read the comments below as well.
Async Progress Bar Update
async / await
is all about not blocking a thread - any thread - when dealing with I/O. Putting a blocking I/O call insideTask.Run()
(like you did inCopy()
) doesn't avoid blocking - it just create a Task which some other thread will later pick up, just to find it itself gets blocked when it hits the blockingCopyFileEx.FileRoutines.CopyFile()
method.- You are getting that error because you are not using
async / await
properly (regardless the above). Think about which thread is trying to modify the UI objectfileProgressBar
: the random threadpool thread that picks up the Task you create onTask.Run()
gets to executefileProgressBar.Value = ...
, which will obviously throw.
This is one way to avoid this situation:
async Task Progress()
{
await Task.Run(() =>
{
//A random threadpool thread executes the following:
while (!complete)
{
if (fileProgress != 0 && totalProgress != 0)
{
//Here you signal the UI thread to execute the action:
fileProgressBar.Invoke(new Action(() =>
{
//This is done by the UI thread:
fileProgressBar.Value = (int)(fileProgress / totalProgress) * 100
}));
}
}
});
}
private async void startButton_Click(object sender, EventArgs e)
{
await Copy();
await Progress();
MessageBox.Show("Done"); //here we're on the UI thread.
}
async Task Copy()
{
//You need find an async API for file copy, and System.IO has a lot to offer.
//Also, there is no reason to create a Task for MyAsyncFileCopyMethod - the UI
// will not wait (blocked) for the operation to complete if you use await:
await MyAsyncFileCopyMethod();
complete = true;
}
C# Progress method executes after the await returns
Unfortunately, this behavior is not unexpected. Progress updates are queued, and can arrive after the work has completed. On the other hand, task completion is synchronous (assuming compatible contexts).
In this case, since you're using IProgress<T>
, then your JustDoItAsync
should not ever need to access the UI directly. So you should be able to use ConfigureAwait(false)
on your await
s in that method, and that should take care of it.
Alternatively, you could keep a "flag", set it when the task is done, and check it in your progress report, but that makes the code rather awkward:
public async void button1_Click(object sender, EventArgs e)
{
Action<int> progressUpdate = ProgressUpdate;
await JustDoItAsync(new Progress<int>(update => progressUpdate?.Invoke(update)));
progressUpdate = null;
textBox1.Text += "\r\nDone!\r\n";
}
The other alternative is to embed the flag right in your IProgress<T>
implementation.
Using IProgress when reporting progress for async await code vs progress bar control
The IProgress<T>
implementation offered natively by the .NET platform, the Progress<T>
class, has the interesting characteristic of notifying the captured SynchronizationContext
asynchronously, by invoking its Post
method. This characteristic sometimes results to unexpected behavior. For example can you guess what effect has the code below to the Label1
control?
IProgress<string> progress = new Progress<string>(s => Label1.Text = s);
progress.Report("Hello");
Label1.Text = "World";
What text will be eventually written to the label, "Hello"
or "World"
? The correct answer is: "Hello"
. The delegate s => Label1.Text = s
is invoked asynchronously, so it runs after the execution of the Label1.Text = "World"
line, which is invoked synchronously.
Implementing a synchronous version of the Progress<T>
class is quite trivial. All you have to do is copy-paste Microsoft's source code, rename the class from Progress<T>
to SynchronousProgress<T>
, and change the line m_synchronizationContext.Post(...
to m_synchronizationContext.Send(...
. This way every time you invoke the progress.Report
method, the call will block until the invocation of the delegate on the UI thread is completed. The unfortunate implication of this is that if the UI thread is blocked for some reason, for example because you used the .Wait()
or the .Result
to wait synchronously for the task to complete, your application will deadlock.
The asynchronous nature of the Progress<T>
class is rarely a problem in practice, but if you want to avoid thinking about it you can just manipulate the ProgressBar1
control directly. After all you are not writing a library, you are just writing code in the event handler of a button to make some HTTP requests. My suggestion is to forget about the .ConfigureAwait(false)
hackery, and just let the main workflow of your asynchronous event handler to stay on the UI thread from start to end. If you have synchronous blocking code that needs to be offloaded to a ThreadPool
thread, use the Task.Run
method to offload it. To create your tasks, instead of manually adding tasks to a List<Task>
, use the handly LINQ Select
operator to project each DataRow
to a Task
. Also add a reference to the System.Data.DataSetExtensions
assembly, so that the DataTable.AsEnumerable
extension method becomes available. Finally add a throttler (a SemaphoreSlim
), so that your application makes efficient use of the available network bandwidth, and it doesn't overburden the target machine:
private async void Button1_Click(object sender, EventArgs e)
{
Button1.Enabled = false;
const int maximumConcurrency = 10;
var throttler = new SemaphoreSlim(maximumConcurrency, maximumConcurrency);
DataTable dataTable = (DataTable)GridView1.DataSource;
ProgressBar1.Minimum = 0;
ProgressBar1.Maximum = dataTable.Rows.Count;
ProgressBar1.Step = 1;
ProgressBar1.Value = 0;
Task[] tasks = dataTable.AsEnumerable().Select(async row =>
{
await throttler.WaitAsync();
try
{
await Task.Delay(3000); // Simulate an asynchronous HTTP request
await Task.Run(() => Thread.Sleep(2000)); // Simulate synchronous code
}
catch
{
await Task.Run(() => Thread.Sleep(1000)); // Simulate synchronous code
}
finally
{
throttler.Release();
}
ProgressBar1.PerformStep();
}).ToArray();
await Task.WhenAll(tasks);
Button1.Enabled = true;
}
C# Tasks with Progress Reporting and Return Value Not Reporting Progress
There is no need for the WaitAll
as already mentioned.
private async Task CallLongRunningTask() {
string fileName = "TestFN";
Progress<string> progress = new Progress<string>();
progress.ProgressChanged += (_, newText) => lbl.Text=newText;
//waiting here for long running task to finish
bool retVal = await Task.Run(() => LongRunningTask(fileName, progress));
// do some other stuff after waiting
}
async-await spreads all the way up. You need to await everything including the calling method.
await CallLongRunningTask();
and so on.
If calling from an event handler, that is the only exception where async void
can be used
private async void OnSomeHandler(object sender, EventArgs args) {
//...
await CallLongRunningTask();
//...
}
Reference Async/Await - Best Practices in Asynchronous Programming
How and when to use ‘async’ and ‘await’
When using async
and await
the compiler generates a state machine in the background.
Here's an example on which I hope I can explain some of the high-level details that are going on:
public async Task MyMethodAsync()
{
Task<int> longRunningTask = LongRunningOperationAsync();
// independent work which doesn't need the result of LongRunningOperationAsync can be done here
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation
{
await Task.Delay(1000); // 1 second delay
return 1;
}
OK, so what happens here:
Task<int> longRunningTask = LongRunningOperationAsync();
starts executingLongRunningOperation
Independent work is done on let's assume the Main Thread (Thread ID = 1) then
await longRunningTask
is reached.Now, if the
longRunningTask
hasn't finished and it is still running,MyMethodAsync()
will return to its calling method, thus the main thread doesn't get blocked. When thelongRunningTask
is done then a thread from the ThreadPool (can be any thread) will return toMyMethodAsync()
in its previous context and continue execution (in this case printing the result to the console).
A second case would be that the longRunningTask
has already finished its execution and the result is available. When reaching the await longRunningTask
we already have the result so the code will continue executing on the very same thread. (in this case printing result to console). Of course this is not the case for the above example, where there's a Task.Delay(1000)
involved.
Howto wrap EAP Pattern Method and IProgress with Task
I ended up with using the follwoing solution from this post:
A reusable pattern to convert event into task.
The functions is now awaitbale, but not cancelbale..coudn't find a way to achieve this so far
I use the TaskExt Class like this in my CompressDirectoryTaskAsync:
public static Task CompressDirectoryTaskAsync(SevenZipCompressor compressor,
CompressItem actionItem, IProgress<CompressItem> progress,
CancellationToken cancellationToken)
{
// Do some stuff with MyClass to get sourcePath and archiveFileName
//
...
// Add Event Handler and Progress
compressor.Compressing += new EventHandler<ProgressEventArgs>((sender, args) =>
{ CompressItem_ProgressChanged(sender, args, actionItem, progress); });
compressor.CompressionFinished += new EventHandler<EventArgs>((sender, args) =>
{ CompressItem_FileCompleted(sender, args, actionItem, progress); });
compressor.FileCompressionStarted += new EventHandler<FileNameEventArgs>((sender, args) =>
{ CompressItem_FileCompressionStarted(sender, args, actionItem, progress); });
// Start Compression
await TaskExt
.FromEvent<EventArgs>()
.WithHandlerConversion(handler => new EventHandler<EventArgs>(handler))
.Start(
handler => compressor.CompressionFinished += handler,
() => compressor.BeginCompressDirectory(actionItem.SourcePath, archiveFileName),
handler => compressor.CompressionFinished -= handler,
cancellationToken).ConfigureAwait(false);
...
...
}
async await return Task
async
methods are different than normal methods. Whatever you return from async
methods are wrapped in a Task
.
If you return no value(void) it will be wrapped in Task
, If you return int
it will be wrapped in Task<int>
and so on.
If your async method needs to return int
you'd mark the return type of the method as Task<int>
and you'll return plain int
not the Task<int>
. Compiler will convert the int
to Task<int>
for you.
private async Task<int> MethodName()
{
await SomethingAsync();
return 42;//Note we return int not Task<int> and that compiles
}
Sameway, When you return Task<object>
your method's return type should be Task<Task<object>>
public async Task<Task<object>> MethodName()
{
return Task.FromResult<object>(null);//This will compile
}
Since your method is returning Task
, it shouldn't return any value. Otherwise it won't compile.
public async Task MethodName()
{
return;//This should work but return is redundant and also method is useless.
}
Keep in mind that async method without an await
statement is not async
.
async/await - when to return a Task vs void?
Normally, you would want to return a
Task
. The main exception should be when you need to have avoid
return type (for events). If there's no reason to disallow having the callerawait
your task, why disallow it?async
methods that returnvoid
are special in another aspect: they represent top-level async operations, and have additional rules that come into play when your task returns an exception. The easiest way is to show the difference is with an example:
static async void f()
{
await h();
}
static async Task g()
{
await h();
}
static async Task h()
{
throw new NotImplementedException();
}
private void button1_Click(object sender, EventArgs e)
{
f();
}
private void button2_Click(object sender, EventArgs e)
{
g();
}
private void button3_Click(object sender, EventArgs e)
{
GC.Collect();
}
f
's exception is always "observed". An exception that leaves a top-level asynchronous method is simply treated like any other unhandled exception. g
's exception is never observed. When the garbage collector comes to clean up the task, it sees that the task resulted in an exception, and nobody handled the exception. When that happens, the TaskScheduler.UnobservedTaskException
handler runs. You should never let this happen. To use your example,
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
Yes, use async
and await
here, they make sure your method still works correctly if an exception is thrown.
For more information see: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
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
.
Related Topics
How Set Value a Property Selector Expression<Func<T,Tresult>>
How to Make the .Net Httpclient Use Http 2.0
C# Invalid Attempt to Call Read When Reader Is Closed
How to Set Datagridview Textbox Column to Multi-Line
How to Password Encrypt SQLite Database
Entity Framework - Capitalizing First Property Name Letter
How to Share Sessions Between PHP and ASP.NET Application
How to Compile C# Application with C++ Static Library
Rijndael 256 Encrypt/Decrypt Between C# and PHP
How to Prevent or Block Closing a Winforms Window
How to Convert Code from C# to PHP
How to Get the Value of a Session Variable Inside a Static Method
Decrypt String in C# That Was Encrypted with PHP Openssl_Encrypt
Visual Studio C# Intellisense Not Automatically Displaying
How to Convert This C# Rijndael Encryption to PHP