2

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?

3
  • 2
    I think you want to break some design rules thus faces problems. The constructors intended to be used to ensure invariant of object. Commented Aug 7, 2015 at 21:05
  • How would I do it the correct way then? Commented Aug 7, 2015 at 21:26
  • It depends what is your intention when you fire and forget the task. Commented Aug 7, 2015 at 21:41

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

But how do you handle the case where you don't have any control on how and when the constructor is called? For example, in a Windows Store app, we would have a Page that will be constructed automatically on app launch. If I want to do some heavy updates on the page every time the page is created, but I have (no ?) control of when and how the page is created, what would I do?
0

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);

5 Comments

Yes, anything inside ContinueWith will be executed on UI thread once the control comes back from MyHeavyCpuMethod.
Then how can I propagate the exception back to the main UI thread? What would happen if I rethrow it inside the ContinueWith()? Say, if I just want to make the app crash, what do I have to do?
invoke the method in your ContinueWith: Invoke((MethodInvoker)(() => MessageBox.Show("ERROR")));
@AliBaig It is not true unless you pass TaskScheduler.FromCurrentSynchronizationContext() to ContinueWith
Oh yes! That parameter is skipped unintentionally :)

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.