I'm trying to catch an exception thrown by a method (GetMoreCodes) run by a Task, but when debugging the exception is never handled and the catch block is never hit. Tried different techniques (in particular with/without await). This code is in an (async) button event handler.
try
{
// Task.Run(() => GetMoreCodes(CodeBufferMaxSize));
// await Task.Run(() => GetMoreCodes(0));
await Task.Run(() => { throw new Exception("test!"); });
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: " + ex);
}
This looks to me like most examples I saw here and in blogs, in particular in this one (http://blog.stephencleary.com/ - big thanks #stephen-cleary).
For now, I only want the application not to crash and log an error if any.
Am I missing something?
await Task.Run(() => {
try
{
throw new Exception("test!");
}
catch (Exception ex)
{
Console.WriteLine("Catched");
}
});
Why not just catched in the Task.Run() ?
If you want to catch it in Console.Application with your code you can do it like this. But t.Wait() is blocking the UI Thread and waits for the Task to finish.
public static void Main(string[] args)
{
try
{
Task t = TestError();
t.Wait();
}
catch (Exception ex)
{
Console.WriteLine("Catched");
}
Console.ReadLine();
}
public static async Task TestError()
{
// Task.Run(() => GetMoreCodes(CodeBufferMaxSize));
// await Task.Run(() => GetMoreCodes(0));
await Task.Run(() => { throw new Exception("test!"); });
}
Related
I have started to explore TPL in .NET to implement a windows service which will run multiple independent methods in parallel with some delay after each successful execution, following is a rough console application I came up with after looking at various examples:
class Program
{
static void Main(string[] args)
{
CancellationTokenSource _ct1 = new CancellationTokenSource();
CancellationTokenSource _ct2 = new CancellationTokenSource();
int count = 0;
var task1 = new Task(async () =>
{
try
{
while (true)
{
DoWork();
await Task.Delay(2000, _ct1.Token);
count++;
if (count >= 5)
{
throw new NotImplementedException();
}
}
}
catch (TaskCanceledException ex)
{
//Log cancellation and do not continue execution
Console.WriteLine("DoWork cancelled: " + ex.Message);
}
catch (Exception ex)
{
//Log error and continue with execution
Console.WriteLine("Error occurred at DoWork");
}
}, _ct1.Token, TaskCreationOptions.LongRunning);
var task2 = new Task(async () =>
{
try
{
while (true)
{
DoWork2();
await Task.Delay(2000, _ct2.Token);
count++;
if (count >= 5)
{
_ct2.Cancel();
}
}
}
catch (TaskCanceledException ex)
{
//Log cancellation and do not continue execution
Console.WriteLine("DoWork2 cancelled: " + ex.Message);
}
catch (Exception ex)
{
//Log error and continue with execution
Console.WriteLine("Error occurred at DoWork");
}
}, _ct2.Token, TaskCreationOptions.LongRunning);
task1.Start();
task2.Start();
Console.ReadKey();
}
public static void DoWork()
{
Console.WriteLine("Doing something...");
}
public static void DoWork2()
{
Console.WriteLine("Doing something else...");
}
}
In the above code when any exception occurs during the task execution, I need to log the error and then continue with the task execution, right now the task stops executing if there is an exception. My questions:
How to handle exceptions properly so that the task execution doesn't stop?
When I add a debugger break point at DoWork() method, the DoWork2() doesn't run which mean the tasks are not running in parallel on separate threads and running on a single thread and blocking each other. How to make sure the Tasks are running independent of each other on separate threads?
PS: The above code is a simple console app just to understand the workings of TPL, so please ignore if there are obvious design problems.
I believe that the simplest chage you can do to make your task1 running after exception is as follows:
var task1 = new Task(async () =>
{
while (true)
{
try
{
DoWork();
await Task.Delay(2000, _ct1.Token);
count++;
if (count >= 5)
{
throw new NotImplementedException();
}
}
catch (TaskCanceledException ex)
{
//Log cancellation and do not continue execution
Console.WriteLine("DoWork cancelled: " + ex.Message);
break;
}
catch (Exception ex)
{
//Log error and continue with execution
Console.WriteLine("Error occurred at DoWork");
count = 0; // without this you'll have exception thrown on each of further iterations
}
}
}, _ct1.Token, TaskCreationOptions.LongRunning);
Meaning that try...catch is moved inside the loop.
Please also note #pull420 comment on while you don't see task2 running
I have the following code, where a Task can be canceled, but I basically need to wait for it to complete (to ensure integrity) before throwing the OperationCanceledException to the caller.
public static void TaskCancellationTest() {
try {
Console.WriteLine("TaskCancellationTest started.");
var cts = new CancellationTokenSource();
var t = Task.Run(() => {
if (cts.Token.IsCancellationRequested) return;
Console.WriteLine("1");
Task.Delay(2000).Wait();
Console.WriteLine("2");
}).ContinueWith(task => {
if (cts.Token.IsCancellationRequested) return;
Console.WriteLine("3");
Task.Delay(2000).Wait();
Console.WriteLine("4");
});
Task.Run(() => {
Task.Delay(1000).Wait();
Console.WriteLine("Cancelling...");
cts.Cancel();
});
t.Wait();
try {
cts.Token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException) {
Console.WriteLine("Gracefully canceled.");
}
Console.WriteLine("TaskCancellationTest completed.");
}
catch (Exception ex) {
Console.WriteLine("TaskCancellationTest... Failure: " + ex);
}
}
The result, as expected, is:
1
Cancelling...
2
Gracefully canceled.
It works, but I would prefer to pass the CancellationToken to the methods as I understand this is a better pattern. I would also like to be able to observe the token inside the method body and to call ThrowIfCancellationRequested() to abort without having to wait for the next ContinueWith().
I was playing with the following alternative code, which also works, but is there any way to have an OperationCanceledException raised instead of an AggregateException?
If I pass the cancellationToken to the WaitAll() method, the problem is that it will throw an OperationCanceledException immediately upon cancellation of the token, rather than waiting for the tasks t1 and t2 to actually complete (they will continue running in the background) and then only throwing the exception.
public static void TaskCancellationTest2() {
try {
Console.WriteLine("TaskCancellationTest2 started.");
var cts = new CancellationTokenSource();
var t1 = Task.Run(() => {
Console.WriteLine("1");
Task.Delay(2000).Wait();
Console.WriteLine("2");
}, cts.Token);
var t2 = t1.ContinueWith(task => {
Console.WriteLine("3");
Task.Delay(2000).Wait();
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("4");
}, cts.Token);
Task.Run(() => {
Task.Delay(1000).Wait();
Console.WriteLine("Cancelling...");
cts.Cancel();
});
try {
try {
Task.WaitAll(t1, t2);
}
catch (AggregateException ae) {
if (ae.InnerExceptions.Count == 1 && ae.InnerExceptions.Single() is OperationCanceledException) {
throw ae.InnerExceptions.Single();
}
throw;
}
}
catch (OperationCanceledException) {
Console.WriteLine("Gracefully canceled.");
}
Console.WriteLine("TaskCancellationTest2 completed.");
}
catch (Exception ex) {
Console.WriteLine("TaskCancellationTest2... Failure: " + ex);
}
}
I have prepared a fiddle here.
This question's title is very similar to mine, but the accepted answer is unfortunately not relevant to my case.
Do you know of any way to achieve what I would like, that makes as good use of CancellationToken as possible?
I think the TPL is designed to eagerly complete tasks if the CancellationToken is set. Part of the reason you are seeing this behavior is because you are calling t.Wait(cts.Token). The overload that takes a CancellationToken will stop waiting if the token is set even if the task hasn't ran to completion.
It's the same with ContinueWith if you pass in a CancellationToken the task can complete as soon as that token is set.
Change your code to call t.Wait() and ContinueWith without a token and you'll get the behavior you want.
public static void TaskCancellationTestNotWorking1()
{
try
{
Console.WriteLine("TaskCancellationTestNotWorking started.");
var cts = new CancellationTokenSource();
var t = Task.Run(() =>
{
Console.WriteLine("1");
Thread.Sleep(2000);
Console.WriteLine("2");
}, cts.Token).ContinueWith(task =>
{
Console.WriteLine("3");
Thread.Sleep(2000);
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("4");
});
Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Cancelling...");
cts.Cancel();
}, cts.Token);
try
{
t.Wait();
}
catch (OperationCanceledException)
{
Console.WriteLine("IsCanceled " + t.IsCanceled);
Console.WriteLine("IsCompleted " + t.IsCompleted);
Console.WriteLine("Gracefully canceled.");
}
catch (AggregateException)
{
Console.WriteLine("IsCanceled " + t.IsCanceled);
Console.WriteLine("IsCompleted " + t.IsCompleted);
Console.WriteLine("Gracefully canceled 1.");
}
Console.WriteLine("TaskCancellationTestNotWorking completed.");
}
catch (Exception ex)
{
Console.WriteLine("TaskCancellationTestNotWorking... Failure: " + ex);
}
}
You might find this article useful How do I cancel non-cancelable async operations?
I tried putting the try catch around the Task.WhenAll(tasks) but it did not catch anything. In one of my tasks I tried to artificially generate an exception by using Substring on an empty string. But all that happens is the app crashes and defaults to Application_UnhandledException.
private async void RunTasks()
{
tasks[0] = HttpExtensions.GetMyData("http://www....");
tasks[1] = HttpExtensions.GetMyData("http://www.....");
tasks[2] = GeoLocate.OneShotGeoLocate();
try
{
await Task.WhenAll(tasks);
}
catch (AggregateException ae)
{
App.ViewModel.ErrorMessage = ae.Message;
}
}
To get all exceptions thrown from execution of the tasks, use the following:
Task taskReturned = Task.WhenAll(taskArray);
try
{
await taskReturned;
}
catch
{
throw taskReturned.Exception;
}
This worked thanks to 'nakiya'
private async void RunTasks()
{
tasks[0] = HttpExtensions.GetMyData("http://www....");
tasks[1] = HttpExtensions.GetMyData("http://www.....");
tasks[2] = GeoLocate.OneShotGeoLocate();
try
{
await Task.WhenAll(tasks);
}
catch (Exception ae)
{
App.ViewModel.ErrorMessage = ae.Message;
}
}
The fact that we can't use the await keyword in catch blocks makes it quite awkward to show error messages from async methods in WinRT, since the MessageDialog API is asynchronous. Ideally I would like be able to write this:
private async Task DoSomethingAsync()
{
try
{
// Some code that can throw an exception
...
}
catch (Exception ex)
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
}
}
But instead I have to write it like this:
private async Task DoSomethingAsync()
{
bool error = false;
try
{
// Some code that can throw an exception
...
}
catch (Exception ex)
{
error = true;
}
if (error)
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
}
}
All methods that need to do this have to follow a similar pattern, which I really don't like, because it reduces the code readability.
Is there a better way to handle this?
EDIT: I came up with this (which is similar to what svick suggested in his comments):
static class Async
{
public static async Task Try(Func<Task> asyncAction)
{
await asyncAction();
}
public static async Task Catch<TException>(this Task task, Func<TException, Task> handleExceptionAsync, bool rethrow = false)
where TException : Exception
{
TException exception = null;
try
{
await task;
}
catch (TException ex)
{
exception = ex;
}
if (exception != null)
{
await handleExceptionAsync(exception);
if (rethrow)
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
}
Usage:
private async Task DoSomethingAsync()
{
await Async.Try(async () =>
{
// Some code that can throw an exception
...
})
.Catch<Exception>(async ex =>
{
var dialog = new MessageDialog("Something went wrong!");
await dialog.ShowAsync();
});
}
.Catch<...> calls can be chained to mimick multiple catch blocks.
But I'm not really happy with this solution; the syntax is even more awkward than before...
you already have that functionality in TPL
await Task.Run(async () =>
{
// Some code that can throw an exception
...
}).ContinueWith(async (a) =>
{
if (a.IsFaulted)
{
var dialog = new MessageDialog("Something went wrong!\nError: "
+ a.Exception.Message);
await dialog.ShowAsync();
}
else
{
var dialog2 = new MessageDialog("Everything is OK: " + a.Result);
await dialog2.ShowAsync();
}
}).Unwrap();
In this machine I don't have Windows 8 so I tested in Windows 7 but I think is the same.
*Edit
as stated in the comments its needed .Unwrap(); in the end for the await to work
C# 6 now supports await in catch and finally, so the code can be written the way I wanted it; a workaround is no longer needed.
I have just installed Visual Studio 2012, So I can finally test C# 5.0 features
like async/await. I was doing some testing and a doubt come to my mind.
What is the best way to handle task Results.
Given the following Snippet:
Task<List<string>> tarea = GetStringListAsync();
tarea.ContinueWith((x) =>
{
if (x.Status == TaskStatus.RanToCompletion)
{
x.Result.ForEach((y) => Console.WriteLine(y));
}
else if (x.Status == TaskStatus.Faulted)
{
Console.WriteLine(x.Exception.InnerException.Message);
}
});
private static async Task<List<string>> GetStringListAsync()
{
return await Task.Run(() =>
{
return GetStringList();
});
}
private static List<string> GetStringList()
{
//I uncomment this to get forced exception
//throw new Exception("Error Occurred");
//Add some delay
System.Threading.Thread.Sleep(12000);
return new List<string>() { "String1", "String2", "String3" };
}
I am handling the task Result in ContinueWith , but I would like to know if there is a better aproach.
Use await instead of ContinueWith or Result:
try
{
List<string> area = await GetStringListAsync();
area.ForEach((y) => Console.WriteLine(y));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
As a side note, you should usually not wrap synchronous methods (GetStringList) with a fake-asynchronous methods (e.g., using Task.Run). Let the caller decide if they want to push it to a background thread:
try
{
List<string> area = await Task.Run(() => GetStringList());
area.ForEach((y) => Console.WriteLine(y));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}