UI freezes when using async await - c#

I have troubles to make my UI work using an async method. Here is a part of my code
private async void btnDoOutput_Click(object sender, RoutedEventArgs e)
{
/* Initiliaze */
groupBoxConfiguration.IsEnabled = false;
var progressIndicator = new Progress<int>();
progressIndicator.ProgressChanged += (s,value) =>
{
progressExport.Value = (double)value;
labelPercentage.Content = "Export in progress : " + value + " %";
};
/* do Work */
switch (something)
{
case 1:
await method(input, output, options, progressIndicator);
break;
default: break;
}
/* Finalization */
groupBoxConfiguration.IsEnabled = true;
}
The method is
public async static Task<string> method(string input, string output, string options, IProgress<int> progress)
{
while(something)
{
//operations on input and output
if (progress != null)
{
progress.Report(percentage);
}
}
}
When I click on my button, the UI freezes, the groupBox is still enabled, the progress is not shown until the end.

I think you are completely misunderstanding how async / await actually works. All of your code is still running on the UI thread because you don't actually tell it otherwise. This means your await on method is pointless because it's going to run synchronously anyway.
The purpose of async/await is to allow the calling code the opportunity to continue processing until it hits part of the code that requires the result of the awaitable task. So in your example, you would need to change your method body to actually return an awaitable Task
public Task method(string input, string output, string options, IProgress<int> progress)
{
return Task.Run(() => {
while(something)
{
//operations on input and output
if (progress != null)
{
progress.Report(percentage);
}
}
});
}

First of all please don't use static methods, especially static workers.
I believe the problem is you're still on your UI thread (I'm making some wild assumptions based on the code you've given). Try using Task<string>.Factory.StartNew(...) which should automatically invoke off the UI thread.
Note may need to use the dispatcher and invoke back onto the UI thread to get your Progress bar working.

Related

Prevent UI from freezing when using Task.Result

I am calling Task.Run(() => DoSomething()).Result which causes the UI to freeze and it happens because am using ".Result". I need Result because i want to return the value.
I don't want the Method StartSomething to be async because I don't want to await the method StartSomething. I want the await to happen at DoSomething().
So basically I need a asynchronous method to be called by a synchronous method, without freezing the UI. Plus I want to return the value from the async method to the top level that is on Button Click.
Can this code be improved or is there any other solution?
private TaskCompletionSource<bool> TaskCompletion = null;
private void Button_Click(object sender, RoutedEventArgs e)
{
bool k = StartSomething();
}
private bool StartSomething()
{
return Task.Run(() => DoSomething()).Result;
}
private async Task<bool> DoSomething()
{
TaskCompletion = new TaskCompletionSource<bool>();
await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
MessageBox.Show("DoSomething");
return true;
}
Method StartSomething() doesn't make sense to me. It starts a new Task and then just synchronously waits for the result (.Result) of this task, which is effectively useless - it is nearly [*] the same as calling DoSomething() directly. Also DoSomething() is already asynchronous so you don't need to start a new Task for it.
It looks like you don't need StartSomething() method at all. If you make Button_Click handler async, you can then simply await DoSomething() directly:
private TaskCompletionSource<bool> TaskCompletion = null;
private async void Button_Click(object sender, RoutedEventArgs e)
{
bool k = await DoSomething();
}
private async Task<bool> DoSomething()
{
TaskCompletion = new TaskCompletionSource<bool>();
await Task.WhenAny(TaskCompletion.Task, Task.Delay(3000));
MessageBox.Show("DoSomething");
return true;
}
Edit:
While using async all the way down solution (as shown above) is IMO the preferred way, if you really can't change calling code to async, I can think of two ways to call async method from synchronous method without blocking UI. First is to manually set up a continuation tasks like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
DoSomething().ContinueWith((task) =>
{
bool k = task.Result;
// use the result
},
// TaskScheduler argument is needed only if the continuation task
// must run on the UI thread (eg. because it access UI elements).
// Otherwise this argument can be omitted.
TaskScheduler.FromCurrentSynchronizationContext());
// Method can exit before DoSomething().Result becomes
// available, which keep UI responsive
}
So you basicly split synchronous method (one split instead of each await) into several parts (continuation lambda methods) linked by .ContinueWith. This is similar to what await does under a hood. Problem is that unlike await (which produces nice and clean code), your code will be full of these continuation lambdas. And it will get much worse when you add exception handling blocks, using blocks, etc.
The second approach is using nested loops, eg. Stephen Toub's WaitWithNestedMessageLoop extension method:
static T WaitWithNestedMessageLoop<T>(this Task<T> task)
{
var nested = new DispatcherFrame();
task.ContinueWith(_ => nested.Continue = false, TaskScheduler.Default);
Dispatcher.PushFrame(nested);
return task.Result;
}
Nested loops are quite advanced technique (I actually never used it) and I don't recommend using it unless you have to.
[*] There are differences in exception handling, executing thread, etc., but these are not relevant to this question.

Why isn't my async code running async?

WARNING I'm a complete newbie with async/await, and so am probably misunderstanding this completely!
I'm trying to work out how this stuff works, and tried a simple bit of code in the view of a WPF window. I added a button click event handler, and added some sync and async methods as follows...
public partial class MainWindow {
private Random _r = new Random(DateTime.Now.Millisecond);
public MainWindow() {
InitializeComponent();
}
private async void Bleah_Click(object sender, RoutedEventArgs e) {
LstMessages.Items.Clear();
AddToMsg("Starting...");
DoSyncStuff();
await DoStuffAsync();
DoMoreStuffSync();
AddToMsg("Done");
}
private void DoSyncStuff() {
int delay = _r.Next(500, 1500);
AddToMsg("DoSyncStuff - waiting for " + delay + "ms");
Thread.Sleep(delay);
AddToMsg("DoSyncStuff - finished");
}
private void DoMoreStuffSync() {
int delay = _r.Next(500, 1500);
AddToMsg("DoMoreStuffSync - waiting for " + delay + "ms");
Thread.Sleep(delay);
AddToMsg("DoMoreStuffSync - finished");
}
private async Task DoStuffAsync() {
await Task.Run(() => {
int delay = _r.Next(500, 1500);
AddToMsg("DoStuffAsync - waiting for " + delay + "ms");
Thread.Sleep(delay);
AddToMsg("DoStuffAsync - finished");
});
}
private void AddToMsg(string msg) {
Dispatcher.BeginInvoke(
new Action(() => { LstMessages.Items.Add(DateTime.Now.ToString("HH:mm:ss.fff") + " - " + msg); }));
}
LstMessages is a ListBox on the window.
When I click the button, I see that the three methods are always executed in the order I call them, irrespective of the length of each delay.
I'm obviously misunderstanding how this stuff works, but after reading around for a few hours, and trying lots of variations of the code, I can't get it to work how I expect.
Please can anyone clarify what I've done wrong here?
All you have to do is drop the await keyword in your code.
To quote a blog post by Eric Lippert:
Whenever a task is “awaited”, the remainder of the current method is signed up as a continuation of the task, and then control immediately returns to the caller. When the task completes, the continuation is invoked and the method starts up where it was before.
By adding in the await keyword, you're effectively saying "once this async method has completed, carry on with the rest of this method".
It might be easier to understand this with methods that return a value. The following program will start off two methods right away, and will await the result of the async method after it calls the sync method. You can try moving the await line around to watch the difference in behavior.
class Program
{
static void Main(string[] args)
{
MainAsync();
Console.ReadKey();
}
static async void MainAsync()
{
var task = GetNumberAsync();
var syncNumber = GetNumber();
var asyncNumber = await task; // moving this line above "GetNumber();" will make these run in order
Console.WriteLine(syncNumber);
Console.WriteLine(asyncNumber);
}
private static int GetNumber()
{
Console.WriteLine("DoSomeWork - started");
Thread.Sleep(1000);
Console.WriteLine("DoSomeWork - finished");
return 11;
}
private static async Task<int> GetNumberAsync()
{
Console.WriteLine("GetNumberAsync - started");
await Task.Delay(1000);
Console.WriteLine("GetNumberAsync - finished");
return 22;
}
}
Try this approach, it appears you were kicking off an async method but immediately waiting for it in the UI thread.
private async void Bleah_Click(object sender, RoutedEventArgs e)
{
LstMessages.Items.Clear();
AddToMsg("Starting...");
DoSyncStuff();
Task t = DoStuffAsync();
DoMoreStuffSync();
await t;
AddToMsg("Done");
}
The important thing to understand is that async and await keywords don't cause additional threads to be created. (Task.Run() CAN move work to another thread). So what's really going on in your code?
So, in your code, the first call to DoSyncStuff() pauses the main thread. Your call to DoStuffAsync() will not even be executed until after DoSyncStuff() fully completes.
Your call to DoStuffAsync is triggered as though it's async - but because you used the await keyword in the caller function 'await DoStuffAsync()', main thread control will return to the Bleah_Click() caller (which for your purposes won't do anything super interesting). Once DoStuffAsync() completes, control returns to Bleah_Click, and DoMoreStuffSync() is executed - which again pauses your main thread.
AS to your question: I can't tell you what you've "done wrong" as you haven't really specified your desired result - if you want to pause your UI thread execution and execute all your functions in the listed order, then you've done everything right.
What you are seeing makes sense, as you are performing all actions on the main UI thread. You need to either create and manage your own Thread/BackgroundWorker object, or submit a method to the ThreadPool
BackgroundWorker
Thread
ThreadPool
Each approach has its own pros and cons, which can be found in other answers here. Give these links a read and try out the examples

How to run codes on background thread in Windows Runtime

I'm using incremental loading to show a ListView items. I run LoadDetails method in the background thread using Task.Run(...) to not busy the UI thread.
But it still blocks the UI thread and it doesn't render UI elements until it finishes the task.
executing LoadDetails method takes around 3 seconds to complete.
private async void LoadItemCounts(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase != 6)
{
throw new Exception("Not in phase 6");
}
var item = args.Item as ItemModel;
var templateRoot = (Grid)args.ItemContainer.ContentTemplateRoot;
var textBlock = (TextBlock)templateRoot.FindName("textBlock");
await Task.Run(() => LoadDetails(textBlock, item.Id));
}
private async Task LoadDetails(TextBlock textBlock, string id)
{
int count = await DataSource.GetItemCounts(id);
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
textBlock.Text = count.ToString();
});
}
How to fix this so it doesn't block the UI thread? thanks.
(It's a Windows Phone Runtime app)
It's not clear from your question how you are measuring the 3 second delay. Is it that the call to GetItemCounts() itself takes 3 seconds? If so, isn't that to be expected? The delay is why you would execute that asynchronously in the first place, isn't it?
The code you posted doesn't really seem quite right. Since your new Task doesn't await the call to LoadDetails(), that task will finish right away, without any synchronization with the actual work being done. Written differently, you could also avoid having to call through the Dispatcher directly.
I would have written it something more like this:
private async void LoadItemCounts(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase != 6)
{
throw new Exception("Not in phase 6");
}
var item = args.Item as ItemModel;
var templateRoot = (Grid)args.ItemContainer.ContentTemplateRoot;
var textBlock = (TextBlock)templateRoot.FindName("textBlock");
await LoadDetails(textBlock, item.Id);
}
private async Task LoadDetails(TextBlock textBlock, string id)
{
int count = await DataSource.GetItemCounts(id);
textBlock.Text = count.ToString();
}
I.e. as long as you keep awaiting on the UI thread, you don't need to invoke via the Dispatcher. Note that the above assumes you need the LoadDetails() method, presumably because you call it from multiple places and some require this particular implementation for some reason. But note that you could have just written the LoadItemCounts() method like this, and left out the LoadDetails() method altogether:
private async void LoadItemCounts(ListViewBase sender, ContainerContentChangingEventArgs args)
{
if (args.Phase != 6)
{
throw new Exception("Not in phase 6");
}
var item = args.Item as ItemModel;
var templateRoot = (Grid)args.ItemContainer.ContentTemplateRoot;
var textBlock = (TextBlock)templateRoot.FindName("textBlock");
textBlock.Text = (await DataSource.GetItemCounts(id)).ToString();
}
It looks like your code is correctly not blocking the UI thread by using await, but since LoadItemDetails() is presumably being called on the UI thread, it won't finish until the method is finished doing its work.
To fix this, just omit the await on the call to Task.Run(), so something like
Task.Run(() => LoadDetails(textBlock, item.Id));
should make LoadItemDetails() return immediately.

How to run and interact with an async Task from a WPF gui

I have a WPF GUI, where I want to press a button to start a long task without freezing the window for the duration of the task. While the task is running I would like to get reports on progress, and I would like to incorporate another button that will stop the task at any time I choose.
I cannot figure the correct way to use async/await/task. I can't include everything I've tried, but this is what I have at the moment.
A WPF window class :
public partial class MainWindow : Window
{
readonly otherClass _burnBabyBurn = new OtherClass();
internal bool StopWorking = false;
//A button method to start the long running method
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
Task slowBurn = _burnBabyBurn.ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);
await slowBurn;
}
//A button Method to interrupt and stop the long running method
private void StopButton_Click(object sender, RoutedEventArgs e)
{
StopWorking = true;
}
//A method to allow the worker method to call back and update the gui
internal void UpdateWindow(string message)
{
TextBox1.Text = message;
}
}
And a class for the worker method:
class OtherClass
{
internal Task ExecuteLongProcedureAsync(MainWindow gui, int param1, int param2, int param3)
{
var tcs = new TaskCompletionSource<int>();
//Start doing work
gui.UpdateWindow("Work Started");
While(stillWorking)
{
//Mid procedure progress report
gui.UpdateWindow("Bath water n% thrown out");
if (gui.StopTraining) return tcs.Task;
}
//Exit message
gui.UpdateWindow("Done and Done");
return tcs.Task;
}
}
This runs, but the WPF function window is still blocked once the worker method starts.
I need to know how to arrange the async/await/task declarations to allow
A) the worker method to not block the gui window
B) let the worker method update the gui window
C) allow the gui window to stop interrupt and stop the worker method
Any help or pointers are much appreciated.
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 an async 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 an async method and await 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 the awaited Task in an async method.
The execution waits at await for the task to finish and returns back its results, without blocking the main thread.
Task.Run queues a Task in the thread pool. (Unless the it's a pure operation.)
i.e. The async method does not run in another thread. async and await 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 awaiting the task you prevent the execution flow within that method from moving past the awaited 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 is Task then await MethodAsync(); returns void
If ReturnType is Task<T> then await MethodAsync(); returns a value of type T
This is called Unwrapping, see the next section (Wrapping and Unrwapping).
If ReturnType is void you can't await 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 has async it will allow you to take advantage of the magic, i.e. you can write await 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.
The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<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 or task.GetAwaiter().GetResult() or task.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
Your use of TaskCompletionSource<T> is incorrect. TaskCompletionSource<T> is a way to create TAP-compatible wrappers for asynchronous operations. In your ExecuteLongProcedureAsync method, the sample code is all CPU-bound (i.e., inherently synchronous, not asynchronous).
So, it's much more natural to write ExecuteLongProcedure as a synchronous method. It's also a good idea to use standard types for standard behaviors, in particular using IProgress<T> for progress updates and CancellationToken for cancellation:
internal void ExecuteLongProcedure(int param1, int param2, int param3,
CancellationToken cancellationToken, IProgress<string> progress)
{
//Start doing work
if (progress != null)
progress.Report("Work Started");
while (true)
{
//Mid procedure progress report
if (progress != null)
progress.Report("Bath water n% thrown out");
cancellationToken.ThrowIfCancellationRequested();
}
//Exit message
if (progress != null)
progress.Report("Done and Done");
}
Now you have a more reusable type (no GUI dependencies) that uses the appropriate conventions. It can be used as such:
public partial class MainWindow : Window
{
readonly otherClass _burnBabyBurn = new OtherClass();
CancellationTokenSource _stopWorkingCts = new CancellationTokenSource();
//A button method to start the long running method
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
var progress = new Progress<string>(data => UpdateWindow(data));
try
{
await Task.Run(() => _burnBabyBurn.ExecuteLongProcedure(intParam1, intParam2, intParam3,
_stopWorkingCts.Token, progress));
}
catch (OperationCanceledException)
{
// TODO: update the GUI to indicate the method was canceled.
}
}
//A button Method to interrupt and stop the long running method
private void StopButton_Click(object sender, RoutedEventArgs e)
{
_stopWorkingCts.Cancel();
}
//A method to allow the worker method to call back and update the gui
void UpdateWindow(string message)
{
TextBox1.Text = message;
}
}
Here is an example using async/await, IProgress<T> and CancellationTokenSource. These are the modern C# and .Net Framework language features that you should be using. The other solutions are making my eyes bleed a bit.
Code Features
Count to 100 over a period of 10 seconds
Display progress on a progress bar
Long running work (a 'wait' period) performed without blocking the UI
User triggered cancellation
Incremental progress updates
Post operation status report
The view
<Window x:Class="ProgressExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="WidthAndHeight" Height="93.258" Width="316.945">
<StackPanel>
<Button x:Name="Button_Start" Click="Button_Click">Start</Button>
<ProgressBar x:Name="ProgressBar_Progress" Height="20" Maximum="100"/>
<Button x:Name="Button_Cancel" IsEnabled="False" Click="Button_Cancel_Click">Cancel</Button>
</StackPanel>
</Window>
The code
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private CancellationTokenSource currentCancellationSource;
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
// Enable/disabled buttons so that only one counting task runs at a time.
this.Button_Start.IsEnabled = false;
this.Button_Cancel.IsEnabled = true;
try
{
// Set up the progress event handler - this instance automatically invokes to the UI for UI updates
// this.ProgressBar_Progress is the progress bar control
IProgress<int> progress = new Progress<int>(count => this.ProgressBar_Progress.Value = count);
currentCancellationSource = new CancellationTokenSource();
await CountToOneHundredAsync(progress, this.currentCancellationSource.Token);
// Operation was successful. Let the user know!
MessageBox.Show("Done counting!");
}
catch (OperationCanceledException)
{
// Operation was cancelled. Let the user know!
MessageBox.Show("Operation cancelled.");
}
finally
{
// Reset controls in a finally block so that they ALWAYS go
// back to the correct state once the counting ends,
// regardless of any exceptions
this.Button_Start.IsEnabled = true;
this.Button_Cancel.IsEnabled = false;
this.ProgressBar_Progress.Value = 0;
// Dispose of the cancellation source as it is no longer needed
this.currentCancellationSource.Dispose();
this.currentCancellationSource = null;
}
}
private async Task CountToOneHundredAsync(IProgress<int> progress, CancellationToken cancellationToken)
{
for (int i = 1; i <= 100; i++)
{
// This is where the 'work' is performed.
// Feel free to swap out Task.Delay for your own Task-returning code!
// You can even await many tasks here
// ConfigureAwait(false) tells the task that we dont need to come back to the UI after awaiting
// This is a good read on the subject - https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
// If cancelled, an exception will be thrown by the call the task.Delay
// and will bubble up to the calling method because we used await!
// Report progress with the current number
progress.Report(i);
}
}
private void Button_Cancel_Click(object sender, RoutedEventArgs e)
{
// Cancel the cancellation token
this.currentCancellationSource.Cancel();
}
}
This is a simplified version of the most popular answer here by Bijan. I simplified Bijan's answer to help me think through the problem using the nice formatting provided by Stack Overflow.
By carefully reading and editing Bijan's post I finally understood: How to wait for async method to complete?
In my case the chosen answer for that other post is what ultimately led me to solve my problem:
"Avoid async void. Have your methods return Task instead of void. Then you can await them."
My simplified version of Bijan's (excellent) answer follows:
1) This starts a task using async and await:
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
// if ExecuteLongProcedureAsync has a return value
var returnValue = await Task.Run(()=>
ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3));
}
2) This is the method to execute asynchronously:
bool stillWorking = true;
internal void ExecuteLongProcedureAsync(MainWindow gui, int param1, int param2, int param3)
{
//Start doing work
gui.UpdateWindow("Work Started");
while (stillWorking)
{
//put a dot in the window showing the progress
gui.UpdateWindow(".");
//the following line blocks main thread unless
//ExecuteLongProcedureAsync is called with await keyword
System.Threading.Thread.Sleep(50);
}
gui.UpdateWindow("Done and Done");
}
3) Invoke the operation which involves a property from gui:
void UpdateWindow(string text)
{
//safe call
Dispatcher.Invoke(() =>
{
txt.Text += text;
});
}
Or,
void UpdateWindow(string text)
{
//simply
txt.Text += text;
}
Closing comments) In most cases you have two methods.
First method (Button_Click_3) calls the second method and has the async modifier which tells the compiler to enable threading for that method.
Thread.Sleep in an async method blocks the main thread. but awaiting a task does not.
Execution stops on current thread (second thread) on await statements until task is finished.
You can't use await outside an async method
Second method (ExecuteLongProcedureAsync) is wrapped within a task and returns a generic Task<original return type> object which can be instructed to be processed asynchronously by adding await before it.
Everything in this method in executed asynchronously
Important:
Liero brought up an important issue. When you are Binding an element to a ViewModel property, the property changed callback is executed in UI thread. So there is no need to use Dispatcher.Invoke. Value changes fired by INotifyPropertyChanged are automatically marshalled back onto the dispatcher.

Observe property change multiple times in one WPF event using MVVM?

I was wanting to do a costly operation and post back to a user 'where' in the state of operation a method was. Basically I am using MVVM to bind an ICommand to a button click event. That event triggers a dialogue for a user, the file they select then is a word document that is parsed, then a form is filled with that word document. The problem I run into with standard operation is that Text displays only the LAST change to the property. I have set breakpoints and I see that the property gets raised, however it seems that ICommand argument waits till ALL WORK is finished and then updates only the last property. Is there a way around this to show posts backs to a user, while the process is happening?
**So essentially what I want is a user to click a button and see "Obtained Word Document", (work then is done) "Parsed Word Document" one after the other as the process completes. NOT the last change when the ICommand finishes. I think the core issue is that the UI is not getting the changes till the stack pauses that is inside either a 'Relay Command'/'Async Relay Command' delegate method. **
XAML:
<TextBox Text="{Binding WordFileLocation}" />
<Button Content="Start Process" Height="20" Command="{Binding AsyncDoCommand}"/>
<TextBox Text="{Binding Text, IsAsync=True}" />
VIEWMODEL:
private Reader _wordReader = new Reader();
private string _ParsedWordString;
private AsyncRelayCommand _DoAsyncCommand;
private string _Text;
private string _WordFileLocation;
public string Text
{
get { return _Text; }
set
{
_Text = value;
RaisePropertyChanged("Text");
}
}
public string WordFileLocation
{
get { return _WordFileLocation; }
set
{
_WordFileLocation = value;
RaisePropertyChanged("WordFileLocation");
}
}
public ICommand AsyncDoCommand
{
get
{
if (_DoAsyncCommand == null)
{
_DoAsyncCommand = new AsyncRelayCommand(async () => await DoIt());
}
return _DoAsyncCommand;
}
}
public async Task DoIt()
{
WordFileLocation = "Somewhere a dialogue selected...";
Text = "Looking....";
await Task.Delay(2000);
Text = "Look at me"; // Works FINALLY....
await GetWordData();
// If I put in the delay below, the Text change will show up. If not it won't. For some reason my setting of Text DOES not show up till a delay is triggered.
//await Task.Delay(100);
await ParseWordData();
}
async Task ParseWordData()
{
try
{
_ParsedWordString = _wordReader.ReadWordDocWithForms(_WordFileLocation);
Text = "Parsed Word Document";
}
catch (Exception)
{
Text = "Could not parse Word Document";
}
}
async Task GetWordData()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Multiselect = false;
dlg.Filter = "Doc Files (*.doc, *.docx)|*.doc;*.docx";
// open dialog
bool ok = (bool)dlg.ShowDialog();
if(ok)
{
try
{
// Get the location from the dialog
WordFileLocation = dlg.FileName;
Text = "Obtained Word Document.";
}
catch (Exception)
{
Text = "Failed Loading Document.";
}
}
else
{
Text = "Could Not Browse for Document.";
}
}
EDIT 8-20-14 12:45 PST:
Tseng is correct except for one thing. I cannot get the UI to accept the async changes UNLESS I force a 'Task.Delay(100)'. It is like the stack wants to auto finish through my two sub methods. I am a total noob at the .NET 4.5 async methods, but I want to use them as they seem to be the preferred way. I am guessing it is my ignorance in understanding the 'Task' and what it does. I have to do a Task return but it seems await does not like to do something as simple as 'await "Loaded"' or similar. So I have tried return types in my signature method like 'void', Task, Task with a simple 'return "Obtained Document"'. None of this updates the Property, UNTILL I call Task.Delay() AFTER the sub method. So it is my ignorance of understanding the async process of why I need to pause to just get an update. The 'ParseWordDocument' is pretty expensive as it is parsing long word documents and on average takes 2 to 5 seconds depending on the doc size as it is parsing out form fills as well as plain text. However even with this delay my text is not getting updated till this sub method is done.
I'd suggest you to use an async command implementation, like AsyncRelayCommand found on the internet.
I use this implementation for one of my own MVVM Projects.
public class AsyncRelayCommand : ICommand {
protected readonly Func<Task> _asyncExecute;
protected readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public AsyncRelayCommand(Func<Task> execute)
: this(execute, null) {
}
public AsyncRelayCommand(Func<Task> asyncExecute, Func<bool> canExecute) {
_asyncExecute = asyncExecute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) {
if(_canExecute == null) {
return true;
}
return _canExecute();
}
public async void Execute(object parameter) {
await ExecuteAsync(parameter);
// notify the UI that the commands can execute changed may have changed
RaiseCanExecuteChanged();
}
protected virtual async Task ExecuteAsync(object parameter) {
await _asyncExecute();
}
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
}
This has the additional benefit, that you can't only run the command async and do UI operations inbetween (i.e. add to ObservableCollection) but you can also notify the UI when the CanExecute status may be changed (i.e. when the command is finished).
Example usage:
public ICommand DoCommand
{
get
{
if(_DoCommand == null)
{
_DoCommand = new AsyncRelayCommand(DoIt);
}
return _DoCommand;
}
}
public async void DoIt() {
WordFileLocation = "Someplace a dialogue selected";
await ParseDocument();
Text = "Parsed Word Document";
await ObtainDocument();
Text = "Obtained Word Document.";
}
Edit:
WPF Command bindings are async/Task aware. If your ICommand.Execute returns Task or Task<T>, then WPF will run them asynchronously.
You really need to make sure that both, criteria are met:
Your DoIt() Method has the async keyword (C# 5.0/.NET 4.5) (or returns Task rather than being void, for .NET 3.5 and 4.0)
You use await for EVERY long processing. If your method returns an awaitable/Task/Task<T> you can await on it. If your methods doesn't, you can still create a new Task and await it
Another example of the DoIt() Method
public Task ParseDocumentAsync()
{
return Task.Run( () => {
// your long processing parsing code here
});
}
public async void DoIt() {
WordFileLocation = "Someplace a dialogue selected";
Text = "Begin";
await ParseDocumentAsync(); // public Task ParseDocumentAsync() { }
Text = "ParseDocumentDone()";
Text = "Wait 3 seconds";
await Task.Delay(3000);
Text = "Run non-Task methods";
Task.Run( () => LongRunningNonAsyncMethod(); );
Text = "LongRunningNonAsyncMethod() finished. Wait 2 seconds";
// DON'T DO THIS. It will block the UI thread!
// It has no await, it runs on the thread which started everything,
// which is UI Thread in this case, because the View invoked the command.
// That's why it locks the UI
Thread.Sleep(2000);
Text = "Waited 2 seconds. We won't see this, because UI is locked";
// DON'T DO THIS, it will ALSO block the UI Thread.
LongRunningNonAsyncMethod();
Text = "Finished";
}
On a side note: If you are using .NET 4.5 and C# 5.0, you can use async/await keywords for async operations. If you are forced to use older Frameworks (.NET 3.5 and 4.0), you can still use Task t = Task.Run(...) to start it and `t.ContinueWith( () => { Text = "Finished" } )´ to execute code after the task is finished.
Edit2:
Sorry for the late reply, I was busy with RL work, didn't had much time to watch in here. I'll update your ParseWordData() method and hope it works then.
// alternatively: async void ParseWordData().
// async void => Task as return type
// async Task => Task<Task> as return type
Task ParseWordData()
{
return Task.Run( () => {
try
{
_ParsedWordString = _wordReader.ReadWordDocWithForms(_WordFileLocation);
Text = "Parsed Word Document";
}
catch (Exception)
{
Text = "Could not parse Word Document";
}
});
}
This will run the ReadWordDocWithForms code inside a thread/Task and return the Task. The Task can be awaited.
Basically it boils down to: use await on awaitable methods (which return Task or Task<T>) and if you need to run a method which isn't awaitable, use Task.Run(...) and return (or await) this Task.
I'm unable to add just a comment so I'll go with an answer.
The ICommand will use the base UI thread to do its processing, so you will not be able to accomplish this without setting up a task of some sort.
It sounds like you know how to do that but in case, this is how I would do it:
Text = "Parsed Word Document";
Task.Factory.StartNew(() =>
{
//do your "DoIt" work here
Text = "Obtained Word Document.";
});
Edit:
public ICommand DoCommand
{
get
{
if (_DoCommand == null)
{
_DoCommand = new RelayCommand(Param => NewMethod(new Action(()=>DoIt())));
}
return _DoCommand;
}
}
NewMethod(Action DoIt)
{
Task.Factory.StartNew(() =>
{
DoIt.Invoke();
});
}
The Linq statement in the RelayComand Is a bit messy, but this does allow you to reuse "NewMethod" in any place you need to pop off a task.
Otherwise, you could simply call DoIt() from the newmethod and save yourself the Action parameter.

Categories

Resources