简体   繁体   中英

Where to handle an exception in a task of async action?

The sample code is like this:

Action action = async () =>
{
    Console.WriteLine("Action start...");
    await Task.Delay(1000);
    throw new Exception("Exception from an async action");
};

Task.Run(action);

Console.ReadKey(); 

Where to handle the exception?

You can handle it inside task itself or outside by the caller, just mind the await on Task.Run, this ensures you catch exception instead of making it a silent death.`

Func<Task> action = async () =>
{
    Console.WriteLine("Action start...");
    await Task.Delay(1000);
    throw new Exception("Exception from an async action");
};

try
{
   await Task.Run(action);
}
catch(Exception ex)
{
    Console.WriteLine(ex.Message);
}

Console.ReadKey(); 

Also, check this post about differences of async/await exception handling

There are two things wrong with the code.

First, it is using an async void delegate, which prevents exceptions from working normally (for more information on avoiding async void , see my MSDN article on async best practices ). It should be using Func<Task> instead of Action (for more information on async-friendly delegate types, see my blog post on synchronous and asynchronous delegate types ):

Func<Task> action = async () =>
{
  Console.WriteLine("Action start...");
  await Task.Delay(1000);
  throw new Exception("Exception from an async action");
};

The second thing wrong is that it's using fire-and-forget when running the delegate on the thread pool. The "forget" part of "fire-and-forget" means "ignore all exceptions". To properly propagate exceptions, the task returned from Task.Run should be awaited (for more information on how await works with tasks, see my blog post on async and await ):

await Task.Run(action);

Since your using Task.Run(action); and you're not awaiting the task object returned, the exception is thrown in another thread and you can't handle it in the caller thread (using ContinueWith for example);

Then You need to handle the exception inside the Action delegate:

Action action = async () =>
{
    try
    {
        Console.WriteLine("Action start...");
        await Task.Delay(1000);
        throw new Exception("Exception from an async action");
    }
    catch(Exception ex)
    {
        // do something
    }
};

Quite often people think that when using async-await several threads are involved, while in fact it is not, unless you specify to do so.

Read Eric Lippert about async-await search somewhere in the middle for async await.

He compares async await with a cook: when toasting bread he can wait until the bread is toasted before boiling water for tea and eggs. It would be more efficient if he started boiling water and then turn back to see if the bread is toasted.

The same happens in your code. During your await task.Delay your thread doesn't start waiting. Instead it goes up its call stack to see if one of the callers (who all must be async!) is not awaiting, and thus can continue processing without the result of its calls. After a while it returns to see if the Task.Delay is finished and continues the next statement where the exception is thrown.

Note that in this scenario there is only one thread involved. Exception catching is done as all other exception catching. Although the catcher can check the call stack, he is not sure what pieces of code are executed and which ones aren't. In this respect there is no big difference with non-async-await

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM