Handle exception when using Task.Run() in UI constructor - c#

I have a constructor that call Task.Run() like this:
public MyPage() {
Task.Run(() => {
MyHeavyCpuMethod();
});
}
Here, MyPage() is the constructor of a UI component, and I don't want MyHeavyCpuMethod() to run on my UI thread, so I offload it with Task.Run() in a fire-and-forget fashion since I don't really care when MyHeavyCpuMethod() finishes.
However, this way if MyHeavyCpuMethod() throws, I can't handle the exception that is in the returned Task.
How can I do error handling in this case?

One option is to use async/await... which doesn't work with a constructor, but which can work in a static method:
public static async Task<MyPage> CreateInstance()
{
await Task.Run(...);
// Anything else asynchronous you want to use
return new MyPage();
}
And then assuming you're using this from an async method, you can just use:
MyPage page = await MyPage.CreateInstance();
That way, if the CPU-bound task fails, you won't even get to the constructor call. The constructor call itself is expected to be fast here, as that will be on the UI thread (as you want it to be).
An alternative to this, you could potentially store the task returned by Task.Run as a field in the page, and then await that post-construction... using the normal async exception handling approaches.

Add a ContinueWith that only fires when the Task doesn't run to completion (throws an exception):
Task.Run(() => MyHeavyCpuMethod())
.ContinueWith(task => { /* some action */ }, TaskContinuationOptions.OnlyOnFaulted);
Your second task will not be run on the UI thread either, per the documentation:
Creates a continuation that executes asynchronously when the target Task completes.
You can play with a dummy method to try it out:
Task.Run(() =>
{
throw new Exception();
}).ContinueWith(t =>
{
Invoke((MethodInvoker)(() => MessageBox.Show("ERROR")));
}, TaskContinuationOptions.OnlyOnFaulted);

Related

Task<T> Extension Method for Returning Result

I was wondering, would there be any gotchas from using an extension method for Task<T> like so:
public static T Await<T>(this Task<T> task)
{
var result = default(T);
Task.Run(async () => result = await task).Wait();
return result;
}
It seems like a decent time saver for those instances where you would like to get the result from a Task but you're in a method that is not marked with async.
You code will not work like you want because you are passing in a "Hot Task" to the function.
I assume the reason you are doing this is to prevent the deadlock of just calling task.Result. The reason the deadlock happens is you are blocking the UI thread and the task's captured sync context uses the UI thread for it's postbacks. The problem is the context is captured when the task starts not when you await it.
So if you did on your UI thread
Task<Foo> task = SomeMethodAsync();
Foo result = task.Await();
you are still going to deadlock because the SynchronizationContext that SomeMethodAsync() captured is the UI context, and any internal await inside SomeMethodAsync() that does not use .ConfiguerAwait(false) will try to use the UI thread which will be blocked by your .Wait() call in Await().
The only way to reliably get it to work is if the method took in a Func<Task<T>> instead of just Task<T>, you could then start the task in the background thread to ensure the sync context is not set.
public static T BlockWithoutLockup<T>(Func<Task<T>> task)
{
T result;
if(SynchronizationContext.Current != null)
{
//We use ".GetAwaiter().GetResult()" instead of .Result to get the exception handling
// like we would if we had called `await` or the function directly.
result = Task.Run(task).GetAwaiter().GetResult();
}
else
{
//If we are on the default sync context already just run the code, no need to
// spin up another thread.
result = task().GetAwaiter().GetResult();
}
return result;
}

Asyc method using await Task.Run() never "completes"

I have a method that is defined as
public async Task SomeAsyncMethod()
{
DoSomeStuff();
await Task.Run(() => {
DoSomeSyncStuff();
DoSomeOtherSyncStuff();
});
var someDebugVariable = "someDebugValue";
}
The method itself does exactly what it is supposed to do and everything runs fine. Yet ... it looks like the "outer" async Task never completes.
Example: When I call it like this
public void CallerMethod()
{
Task t = SomeAsyncMethod();
t.Wait();
}
the t.Wait() never completes. Furthermore: if I place a breakpoint at the assignment of someDebugVariable it never gets hit.
I might add that DoSomeSyncStuff and DoSomeOtherSyncStuff really do what they are supposed to and debugging through them tells me that they both complete.
To prove my point I modified my method like this, and the results are still the same.
public async Task SomeAsyncMethod()
{
DoSomeStuff();
await Task.Run(() => {
/*
DoSomeSyncStuff();
DoSomeOtherSyncStuff();
*/
var a = 2; var b = 3; var c = a + b;
});
var someDebugVariable = "someDebugValue";
}
EDIT
I have tried removing everything but the await Task.Run and it does not change anything. It still does not complete.
The application is a WPF application. The caller thread is the UI thread.
What am I missing here?
The t.Wait() call is causing a deadlock, and also makes the async call entirely pointless. I believe if you change the code to
await Task.Run(() => {
// ...
}).ConfigureAwait(false);
You can fix the deadlock and let the code proceed, but you should really get rid of the t.Wait() call. Anything that needs to be done with the results of the sync function calls should be done after the awaited task, not after the call of the async function.
More in depth:
task.Wait() will block all execution on the main thread while the task is running. When the await task completes, it tries to marshall back to the main thread, but the main thread is blocked! Since A is waiting for B, and B is waiting for A, you get a deadlock.
See: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

RunAsync - How do I await the completion of work on the UI thread?

When awaiting Dispatcher.RunAsync the continuation occurs when the work is scheduled, not when the work has completed. How can I await the work completing?
Edit
My original question assumed the premature continuation was caused by the design of the API, so here's the real question.
When awaiting Dispatcher.RunAsync using an asynchronous delegate, using await within the delegate's code, the continuation occurs when the await is encountered, not when the work has completed. How can I await the work completing?
Edit 2
One reason you may need to dispatch work that's already on the UI thread is to workaround subtle timing and layout issues. It's quite common for values of sizes and positions of elements in the visual tree to be in flux and scheduling work for a later iteration of the UI can help.
I found the following suggestion on a Microsoft github repository: How to await a UI task sent from a background thread.
Setup
Define this extension method for the CoreDispatcher:
using System;
using System.Threading.Tasks;
using Windows.UI.Core;
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher dispatcher,
Func<Task<T>> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
{
var taskCompletionSource = new TaskCompletionSource<T>();
await dispatcher.RunAsync(priority, async () =>
{
try
{
taskCompletionSource.SetResult(await func());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
return await taskCompletionSource.Task;
}
// There is no TaskCompletionSource<void> so we use a bool that we throw away.
public static async Task RunTaskAsync(this CoreDispatcher dispatcher,
Func<Task> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>
await RunTaskAsync(dispatcher, async () => { await func(); return false; }, priority);
}
Once you do that, all you need to do is use the new RunTaskAsync method to have your background task await on the UI work.
Usage example
Let's pretend that this is the method that needs to run in the UI thread. Pay attention to the debug statements, which will help follow the flow:
public static async Task<string> ShowMessageAsync()
{
// Set up a MessageDialog
var popup = new Windows.UI.Popups.MessageDialog("Question", "Please pick a button to continue");
popup.Commands.Add(new Windows.UI.Popups.UICommand("Button 1"));
popup.Commands.Add(new Windows.UI.Popups.UICommand("Button 2"));
popup.CancelCommandIndex = 0;
// About to show the dialog
Debug.WriteLine("Waiting for user choice...");
var command = await popup.ShowAsync();
// Dialog has been dismissed by the user
Debug.WriteLine("User has made a choice. Returning result.");
return command.Label;
}
To await that from your background thread, this is how you would use RunTaskAsync:
// Background thread calls this method
public async void Object_Callback()
{
Debug.WriteLine("Object_Callback() has been called.");
// Do the UI work, and await for it to complete before continuing execution
var buttonLabel = await Dispatcher.RunTaskAsync(ShowMessageAsync);
Debug.WriteLine($"Object_Callback() is running again. User clicked {buttonLabel}.");
}
The output then looks like this:
Object_Callback() has been called.
Waiting for user choice...
User has made a choice. Returning result.
Object_Callback() is running again. User clicked Button 1.
Your question is assuming that you want to schedule (and wait for) work on a UI thread from a background thread.
You'll usually find your code is much cleaner and easier to understand (and it will definitely be more portable) if you have the UI be the "master" and the background threads be the "slaves".
So, instead of having a background thread await some operation for the UI thread to do (using the awkward and unportable Dispatcher.RunAsync), you'll have the UI thread await some operation for the background thread to do (using the portable, made-for-async Task.Run).
You can wrap the call to RunAsync in your own asynchronous method that can be awaited and control the completion of the task and thus the continuation of awaiting callers yourself.
Since async-await is centred on the Task type, you must orchestrate the work using this type. However, usually a Task schedules itself to run on a threadpool thread and so it cannot be used to schedule UI work.
However, the TaskCompletionSource type was invented to act as a kind of puppeteer to an unscheduled Task. In other words, a TaskCompletionSource can create a dummy Task that is not scheduled to do anything, but via methods on the TaskCompletionSource can appear to be running and completing like a normal job.
See this example.
public Task PlayDemoAsync()
{
var completionSource = new TaskCompletionSource<bool>();
this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
try
{
foreach (var ppc in this.Plots.Select(p => this.TransformPlot(p, this.RenderSize)))
{
// For each subsequent stroke plot, we need to start a new figure.
//
if (this.Sketch.DrawingPoints.Any())
this.Sketch.StartNewFigure(ppc.First().Position);
foreach (var point in ppc)
{
await Task.Delay(100);
this.Sketch.DrawingPoints.Add(point.Position);
}
}
completionSource.SetResult(true);
}
catch (Exception e)
{
completionSource.SetException(e);
}
});
return (Task)completionSource.Task;
}
Note: the main work being done on the UI thread is just some lines being drawn on screen every 100ms.
A TaskCompletionSource is created as the puppet master. Look near the end and you'll see that it has a Task property that is returned to the caller. Returning Task satisfies the compilers needs and makes the method awaitable and asynchronous.
However, the Task is just a puppet, a proxy for the actual work going on in the UI thread.
See how in that main UI delegate I use the TaskCompletionSource.SetResult method to force a result into the Task (since returned to the caller) and communicate that work has finished.
If there's an error, I use SetException to 'pull another string' and make it appear that an exception has bubbled-up in the puppet Task.
The async-await subsystem knows no different and so it works as you'd expect.
Edit
As prompted by svick, if the method was designed to be callable only from the UI thread, then this would suffice:
/// <summary>
/// Begins a demonstration drawing of the asterism.
/// </summary>
public async Task PlayDemoAsync()
{
if (this.Sketch != null)
{
foreach (var ppc in this.Plots.Select(p => this.TransformPlot(p, this.RenderSize)))
{
// For each subsequent stroke plot, we need to start a new figure.
//
if (this.Sketch.DrawingPoints.Any())
this.Sketch.StartNewFigure(ppc.First().Position);
foreach (var point in ppc)
{
await Task.Delay(100);
this.Sketch.DrawingPoints.Add(point.Position);
}
}
}
}
A nice way to work the clean way #StephenCleary suggests even if you have to start from a worker thread for some reason, is to use a simple helper object. With the object below you can write code like this:
await DispatchToUIThread.Awaiter;
// Now you're running on the UI thread, so this code is safe:
this.textBox.Text = text;
In your App.OnLaunched you have to initialize the object:
DispatchToUIThread.Initialize(rootFrame.Dispatcher);
The theory behind the code below you can find at await anything;
public class DispatchToUIThread : INotifyCompletion
{
private readonly CoreDispatcher dispatcher;
public static DispatchToUIThread Awaiter { get; private set; }
private DispatchToUIThread(CoreDispatcher dispatcher)
{
this.dispatcher = dispatcher;
}
[CLSCompliant(false)]
public static void Initialize(CoreDispatcher dispatcher)
{
if (dispatcher == null) throw new ArgumentNullException("dispatcher");
Awaiter = new DispatchToUIThread(dispatcher);
}
public DispatchToUIThread GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get { return this.dispatcher.HasThreadAccess; }
}
public async void OnCompleted(Action continuation)
{
if (continuation == null) throw new ArgumentNullException("continuation");
await this.dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => continuation());
}
public void GetResult() { }
}

How do I create a Task that uses await inside the body that behaves the same as the synchronous version when Wait is called?

I have some code that creates a task that does some slow work like this:
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
In the real implementation, the Thread.Sleep will actually be a web service call. I would like to change the body of the method can use await (so it does not consume a thread during the network access/sleep). My first attempt (based on shotgun-debugging the compile errors) was this:
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
However; this task doesn't seem to behave the same as the first one, because when I call .Wait() on it; it returns immediately.
Below is a full sample (console app) showing the differences (the app will end immediately when the second task starts).
What do I need to do so that I can call Start and Wait on a Task which happens to have code using await inside it? The tasks are queued and executed later by an agent, so it's vital that the task is not auto-started.
class Program
{
static void Main(string[] args)
{
var w1 = wait1();
w1.Start();
w1.Wait(); // This waits 110 seconds
var w2 = wait2();
w2.Start();
w2.Wait(); // This returns immediately
}
public static Task wait1()
{
return new Task(() =>
{
Console.WriteLine("Waiting...");
Thread.Sleep(10000);
Console.WriteLine("Done!");
});
}
public static Task wait2()
{
return new Task(async () =>
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
});
}
}
It seems like this isn't possible! See alexm's answer here:
Tasks returned by async methods are always hot i.e. they are created in Running state.
:-(
I've worked around this by making my agent queue Func<Task>s instead, and the overload that receives a task simply queues () => task. Then; when de-queing a task, I check if it's not running, and if so, start it:
var currentTask = currentTaskFunction();
if (currentTask.Status == TaskStatus.Created)
currentTask.Start();
It seems a little clunky to have to do this (if this simple workaround works; why the original restriction on async methods always being created hot?), but it seems to work for me :-)
You could write this as:
public static async Task Wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(10000);
Console.WriteLine("Done!");
}
In general, it's rarely a good idea to ever use new Task or new Task<T>. If you must launch a task using the ThreadPool instead of using the async/await language support to compose one, you should use Task.Run to start the task. This will schedule the task to run (which is important, tasks should always be "hot" by conventions).
Note that doing this will make it so you don't have to call Task.Start, as well.
To help you understand this realize that async / await essentially does not create a new thread but rather it schedules that portion of code to be ran at an available point in time.
When you create the new Task(async () => ...) you have a task that run an async method. When that inner async method hits an await the 'new Task' is considered complete because the rest of it has been scheduled. To help you understand better place some code (a lot if wanted) in the 'new Task' before the await command. It will all execute before the application terminates and once await is reached that task will believe it has completed. It then returns and exits the application.
The best way to avoid this is to not place any task or async methods inside of your task.
Remove the async keyword and the await keyword from the method and it will work as expected.
This is the same as creating a callback if you're familiar with that.
void MethodAsync(Action callback)
{
//...some code
callback?.Invoke();
}
//using this looks like this.
MethodAsync(() => { /*code to run when complete */});
//This is the same as
Task MethodAsync()
{
//... some code here
}
//using it
await MethodAsync();
/*code to run when complete */
The thing to understand is that you're creating a new task within a task basically. So the inner 'callback' is being created at the await keyword.
You're code looks like this..
void MethodAsync(Action callback)
{
//some code to run
callback?.Invoke(); // <- this is the await keyword
//more code to run.. which happens after we run whoever is
//waiting on callback
}
There's code missing obviously. If this doesn't make sense please feel free to contact me and I'll assist. async / await (meant to make things simpler) is a beast to wrap your head around at first. Afterward you get it then it'll probably be your favorite thing in c# since linq. :P
Try this:
public async static Task wait2()
{
Console.WriteLine("Waiting...");
await Task.Delay(2000);
Console.WriteLine("Done!");
}
But we aware that the task is already started so you don't have to call start:
var w2 = wait2();
//w2.Start();
w2.Wait();
I think the problem with your wait2 function is that is creating 2 task, the one in new Task(...) and another in Task.Delay(). You are waiting for the first one, but you are not waiting for the inner one.

Chaining Parallel Tasks to an End Condition or Error Condition

I'm trying to wrap my head around some of the syntax and structure for parallel tasks in C#, specifically around chaining multiple tasks and handling errors.
The specific sequence of steps I'm looking to create are:
Spawn a parallel task and immediately return to the UI.
In the parallel task:
Do Process1()
If Process1() completes without error, do Process2()
If Process2() completes without error, do Process3()
If all tasks complete without error, do SuccessCondition()
If any task resulted in an error, do ErrorCondition()
It's my understanding that I would create a Task and call ContinueWith() to chain more tasks, passing in a TaskContinuationOptions flag to determine when to do that continuation. Additionally, that success/error conditions would fall through all of the continuations to the end. So I'm currently trying this:
var task = new Task(() => Process1());
var task2 = task.ContinueWith(t => Process2(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task3 = task2.ContinueWith(t => Process3(), TaskContinuationOptions.OnlyOnRanToCompletion);
var task4 = task3.ContinueWith(t => SuccessCondition(t), TaskContinuationOptions.OnlyOnRanToCompletion);
var task5 = task4.ContinueWith(t => ErrorCondition(t), TaskContinuationOptions.NotOnRanToCompletion);
task.Start();
It appears to be behaving as expected, except that within ErrorCondition() the instance of t doesn't appear to have an exception, even if I manually threw one from within, say, Process2(). Looking at the MSDN article for handling exceptions, it says to do this:
try
{
task.Wait();
}
catch (AggregateException ex)
{
// Handle exceptions from a collection on ex
}
However, I tried that and it doesn't seem to have the exceptions there either. Also, by calling .Wait() in the main thread like that, am I negating the parallelism and just blocking? It appears that way in my tests.
So I guess my question is... What's the correct way to chain dependent tasks and handle an overall success/error condition here? Or, how should exceptions thrown from within these tasks be properly caught, while still returning immediately to the UI?
Note that if you're using .NET 4.5 and can use async/await, you can do this much more cleanly:
public async Task DoProcess()
{
try
{
await Task.Run(Process1);
await Task.Run(Process2);
await Task.Run(Process3);
SuccessCondition();
}
catch (Exception ex)
{
ErrorCondition(ex);
}
}
If you launch this from the UI thread, SuccessCondition and ErrorCondition will occur on the UI Thread as well. One functional difference here is that the exception you catch will not be an AggregateException; it will instead be the actual exception thrown during the awaited call that failed.
You would need to add a continuation to all of the tasks, 1-4, with the error handling case to allow an error in any of them to call that function.
For convenience you could create a method to add the same continuation to a collection of tasks. Here's one (feel free to add others for the other overloads of ContinueWith as needed):
public static IEnumerable<Task> ContinueWith(this IEnumerable<Task> tasks
, Action<Task> continuation, TaskContinuationOptions options)
{
return tasks.Select(task => task.ContinueWith(continuation, options))
.ToList();//important for this ToList to be here;
//we want the continuations to be added now, not when the result is iterated
}
This allows you to write:
var errorTasks = new[]{task, task2, task3, task4}
.ContinueWith(ErrorCondition, TaskContinuationOptions.NotOnRanToCompletion);
Error handling for tasks is made so much easier in C# 5.0 with async methods though. It would allow you to transform your code into this:
public static async Task Foo()
{
try
{
await Task.Run(Process1())
await Task.Run(Process2())
await Task.Run(Process3())
SuccessCondition();
}
catch (SomeExceptionType ex)
{
HandleException(ex);
}
}
This functions just as you would think it does as per your requirements, which is awesome.

Categories

Resources