0

I want status updates from a long running method. Usually I'd use the dispatcher to post back to the UI thread, but I'm curious about using async await.

To keep it simple:

Create a window, add a button

<Button Name="ButtonWithCodeBehind" Height="25" Click="ButtonWithCodeBehindOnClick"/>

add some code behind onClick handler

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
    await Task.Factory.StartNew(() =>
    {
        ButtonWithCodeBehind.Content = "First";
        Thread.Sleep(1000);
        ButtonWithCodeBehind.Content = "Second";
        Thread.Sleep(1000);
        ButtonWithCodeBehind.Content = "Third";
    });
}

This will obviously break because ButtonWithCodeBehind.Content will be accessed on the wrong thread.

Is there a way to make this work without doing something like:

Deployment.Current.Dispatcher.BeginInvoke(()=>ButtonWithCodeBehind.Content = "Second");

The critical thing here is the long running task will be generating updates as it progresses, I can refactor the code to something like this:

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
    var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
    await Task.Factory.StartNew(() =>
    {
        Task.Factory.StartNew(() => Thread.Sleep(1000))
            .ContinueWith(t => ButtonWithCodeBehind.Content = "First", scheduler)
            .ContinueWith(t => Thread.Sleep(1000))
            .ContinueWith(t => ButtonWithCodeBehind.Content = "Second", scheduler)
            .ContinueWith(t => Thread.Sleep(1000))
            .ContinueWith(t => ButtonWithCodeBehind.Content = "Third", scheduler);
    });
}

But this is fugly. Also, if you took out the async and await keywords and replaced them with Task.WaitAll, it would still execute as expected.

Note: If you're wondering why I'm using Thread.Sleep instead of Task.Delay, I'm actually testing this in Silverlight as well and the async await support doesn't include .Delay (or at least not where I expect it to be).

2 Answers 2

2

If you can split up your long running task into two distinct long running actions (like two Thread.Sleeps in your example above) you can await each long running task on it's own. So the UI updates will be performed on the UI thread.

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
  ButtonWithCodeBehind.Content = "First";
  await Task.Run(() => Thread.Sleep(1000));
  ButtonWithCodeBehind.Content = "Second";
  await Task.Run(() => Thread.Sleep(1000));
  ButtonWithCodeBehind.Content = "Third";
}
Sign up to request clarification or add additional context in comments.

2 Comments

to simulate work Task.Run+ Thread.Sleep is fine. for "waiting a little" Task.Delay is the way to go though
Thanks Andreas, the Thread.Sleep was because I was using Silverlight at the time and Silverlight doesn't include a Task.Delay :(
1

The only part that needs to be awaited is the long-running part - the IO call or, in this instance, the CPU-bound sleep.

private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
    ButtonWithCodeBehind.Content = "First";
    await Task.Factory.StartNew(() => Thread.Sleep());
    ButtonWithCodeBehind.Content = "Second";
    await Task.Factory.StartNew(() => Thread.Sleep());
    ButtonWithCodeBehind.Content = "Third";
}

Await captures the synchronization context and ensures that the rest of the method is signed up to a continuation which is run on a thread with the same context. In WPF, the UI thread handles the code ButtonWithCodeBehindOnClick and thus, by default, will be responsible for the rest of the method call after await.

You can override this default behaviour by configuring the await on the task:

 await Task.Factory.StartNew(() =>
    Thread.Sleep()).ConfigureAwait(continueOnCapturedContext: false);

However, you absolutely don't want to do this in WPF as a threadpool thread will try to update your UI.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.