How to capture AggregateException? [duplicate] - c#

Just noticed strange thing: to catch exception in caller from new Task, lambda MUST be marked as async!? Is it really necessary even if delegate has no await operators at all?
try
{
//Task.Run(() => // exception is not caught!
Task.Run(async () => // unnecessary async!?!
{
throw new Exception("Exception in Task");
}).Wait();
}
catch (Exception ex)
{
res = ex.Message;
}
Why there is neccesary for async operator?
All documentation i can find tells that delegate must not return Void and Task must be awaited for exception to propogate up to caller.
Added full code:
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.Run();
}
public void Run()
{
string result;
try
{
result = OnSomeEvent((s, ea) => RunSomeTask());
}
catch (Exception ex) // Try to catch unhandled exceptions here!
{
result = ex.Message;
}
Console.WriteLine(result);
Console.ReadKey();
}
// Some other Framework bult-in event (can not change signature)
public string OnSomeEvent(EventHandler e)
{
e.Invoke(null, new EventArgs());
return "OK";
}
private async Task RunSomeTask()
{
await Task.Run(async () => // do not need async here!!!
//await Task.Run(() => // caller do not catches exceptions (but must)
{
throw new Exception("Exception in Task1");
});
}
}
So the qestion is how to catche ex. without asyn keyword???

Methods that return Task - such as Task.Run or async methods - will place any exceptions on that returned Task. It's up to you to observe that exception somehow. Normally this is done with await, like this:
await Task.Run(() => { throw ... });
In your case, the problem is in this line:
result = OnSomeEvent((s, ea) => RunSomeTask());
In this code, RunSomeTask is returning a Task, and that Task is never awaited. In order to observe the exception, you should await that task.

When using async/await, exceptions are automatically unwrapped at the site of the await. When using a Task and .Wait(), any exception are wrapped when they come out of the Task, and thus getting information requires you to dig into the Task.Exception property, since they do not propagate up the call stack.
See https://dotnetfiddle.net/MmEXsT

Related

How to propagate an Exception from a Task / Thread to the method that created this Task in c#?

I do not know how I should properly propagate an exception from a Task to the thread that created this task:
private void threadMT()
{
Task task;
try
{
task = new Task(() =>
{
throw new Exception("blabla");
});
task.Start();
while(!task.IsCompleted)
Thread.Sleep(500);
if (task.IsFaulted)
throw task.Exception;
}
catch (Exception ex)
{
throw ex;
}
}
When this line is reached:
throw new Exception("blabla");
the app halts saying that the exception is not handled.
Can it be propagated back to method?
Thx in advance.
The easiest way for you to propagate an exception from a Task executed on the thread-pool is to turn it to actually return a Task which you can await on:
public async Task AwaitOnTaskAsync()
{
try
{
await DoStuffWithThreadAsync();
}
catch (Exception e)
{
}
}
public Task DoStuffWithThreadAsync()
{
return Task.Run(() => { throw new Exception("blabla"); });
}
await will make sure to unwrap the exception out of the Task, allowing you to apply a try-catch on it.
Side Note - Don't use the Task constructor, instead use Task.Run to return a "hot task" (one which has already started). There's no point in creating a Task which you're actively blocking on using Thread.Sleep later on, either execute it synchronously or use async-await to asynchronously wait on the task.

How to propagate an exception from ContinueWith to the calling context of an infinite loop task

I have an infinite loop in a task. Under certain circumstances, this task throws an exception and terminates. Consider the following code snippet.
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
int x = await FirstTask();
window.Title = "FirstTask completed with " + x.ToString();
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}
public async Task<int> FirstTask()
{
Task<int> secondTask;
int result;
secondTask = SecondTask();
textbox.Text = "Awaiting SecondTask result";
result = await secondTask;
textbox.Text = result;
secondTask.ContinueWith(async (Task t) =>
{
var thirdTask = ThirdTask();
thirdTask.ContinueWith(
async (m) =>
await Task.Run(() =>
{
throw thirdTask.Exception.InnerException;
}),
TaskContinuationOptions.OnlyOnFaulted);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
return 5;
}
public async Task<int> SecondTask()
{
await Task.Delay(1500);
return 8;
}
public async Task ThirdTask()
{
while (true)
{
await Task.Delay(500);
throw new ArgumentException("thirdException");
}
}
My problems lies in the inability to propagate the exception thrown from ThirdTask to the Button_Click event. Obviously, awaiting it is not an options, since it is an ongoing infinite operation (this is only simplified to fail quickly). I have, however, no problem with awaiting the "short" task which re-throws the exception, if it is only triggered once the ThirdTask fails. Note that I'm not interested in the doings of the ThirdTask unless it fails, that is while I'm able to await the FirstTask in the event handler.
Experimenting showed that even the most simple example doesn't propagate the exception from the ContinueWith block.
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
Task task = Task.Run(async () => { await Task.Delay(1000); });
task.ContinueWith( (t) => { throw new ArgumentException("test"); }, TaskContinuationOptions.OnlyOnRanToCompletion);
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}
So, how do I propagate an exception from ContinueWith to the calling context, given that the task that throws it has an infinite loop, which prevents me from awaiting it?
The problem I'm trying to solve is two-fold:
First, I need to initialize a resource (FirstTask), in order to do that, I first need to fetch it (SecondTask) and then to begin a process with it (ThirdTask), finally, the initialization of the resource (FirstTask) returns a value indicating the state of the resource, which doesn't depend on the process (ThirdTask). The process (ThirdTask) repeatedly invokes another task (in this case Task.Delay) and performs some work on it, but it can fail. In that case, it throws an exception which needs to be handled.
The second part is the general case of the second code example, of how to throw an exception from ContinueWith to be handled by the calling context.
given that the task that throws it has an infinite loop, which prevents me from awaiting it?
That in no way prevents you from awaiting it. The [easiest] way to handle the case that it throws an exception is specifically to await it.
You can simply implement the method as such:
public async Task FirstTask()
{
Task<int> secondTask = SecondTask();
textbox.Text = "Awaiting SecondTask result";
textbox.Text = await secondTask;
await ThirdTask();
}
If the click handler needs to both update a texbox with the results of the second operation and update the UI if the third fails, then you need to not wrap both of those operations in FirstTask and call them directly from the click handler:
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
textbox.Text = "Awaiting SecondTask result";
int x = await SecondTask();
window.Title = "SecondTask completed with " + x.ToString();
await ThirdTask();
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}

How can a ContinueWith task catch exception of C# task that returns void?

How can a ContinueWith task catch the exception of a C# async task that returns void? (We're using VS 2010, so no async/await keywords yet). Our standard pattern when the task returns something is:
Task<int> task = ...;
task.ContinueWith(t =>
{
try
{
int result = task.Result; //This will throw if there was an error.
//Otherwise keep processing
}
catch(Exception e)
{ ... }
}, TaskScheduler.FromCurrentSynchronizationContext());
But if the task doesn't return anything, there is no "task.Result". So what's the best way to handle that task that doesn't return anything?
EDIT: Here's what I want to accomplish:
Task taskWithNoReturnType = ...
taskWithNoReturnType.ContinueWith( t =>
{
try
{
//HOW CAN I KNOW IF THERE WAS AN EXCEPTION ON THAT TASK???
//Otherwise, keep processing this callback
}
catch(Exception e)
{ ... }
}, TaskScheduler.FromCurrentSynchronizationContext());
Task.Exception gets the AggregateException that caused the Task to end prematurely. If the Task completed successfully or has not yet thrown any exceptions, this will return null.
Example:
Task.Factory
.StartNew(
() => { DoSomething(); /* throws an exception */ } )
.ContinueWith(
p =>
{
if (p.Exception != null)
p.Exception.Handle(x =>
{
Console.WriteLine(x.Message);
return true;
});
});
Found the answer --
Task taskWithNoReturnType = ...
taskWithNoReturnType.ContinueWith( t =>
{
try
{
t.Wait(); //This is the key. The task has already completed (we
//are in the .ContinueWith() after all) so this won't really wait,
//but it will force any pending exceptions to propogate up and
//then the catch will work normally.
//Else, if we get here, then there was no exception.
//<process code normally here>
}
catch(Exception e)
{ ... }
}, TaskScheduler.FromCurrentSynchronizationContext());

What if not await the task?

Here is my code:
private static Stopwatch _stopwatch;
static void PrintException(Exception ex)
{
Console.WriteLine(_stopwatch.Elapsed);
Console.WriteLine(ex);
}
static void ThrowException1()
{
throw new InvalidAsynchronousStateException();
}
static void ThrowException2()
{
throw new NullReferenceException();
}
static async Task ExecuteTask1()
{
await Task.Delay(1000);
ThrowException1();
}
static async Task ExecuteTask2()
{
await Task.Delay(2000);
ThrowException2();
}
static async Task Execute()
{
var t1 = ExecuteTask1();
var t2 = ExecuteTask2();
try
{
await t2;
}
catch (NullReferenceException ex)
{
// the NullReferenceException will be captured
Console.WriteLine("==============");
PrintException(ex);
}
}
static void Main(string[] args)
{
TaskScheduler.UnobservedTaskException += (sender, ev) => PrintException(ev.Exception);
_stopwatch = Stopwatch.StartNew();
Execute();
while (true)
{
Thread.Sleep(5000);
GC.Collect();
}
}
Actually, I didn't await t1 in Execute method, but it seems it was still executed, since I captured the AggregateException about five seconds later.
Can someone tell me when the t1 was executed? In my case, the exceptions order which printed to the Console is 1. NullReferenceException 2. AggregateException
In the async/await world, tasks are "hot". So, when you call ExecuteTask1, the task returned to you is already being processed. It has already started at that point. You can put a Console.WriteLine at the beginning of ExecuteTask* to see that they do start immediately.
await is only used to (asynchronously) wait for the completion of a task. It does not start tasks.
I have an async intro on my blog that you may find helpful.
The task was executed at the point you called ExecuteTask1 here:
var t1 = ExecuteTask1();
You don't need to await a task for it to execute, it will run anyway... you await the task if you want your code to resume executing only after the task has finished otherwise your code will continue running immediately after the task has started running without waiting for it to finish.
If you do not await a task it will still execute, your current execution context will just not "wati for it".
This means you have no direct control over the task and if something goes wrong "inside the task" the exception will not directly propagate to your execution context as it would do when using await or t1.Wait().
In general exceptions which are thrown inside a task are boxed inside an AggregateException, so you can not do the following:
catch (NullReferenceException ex)
You need to do something like and check e.g. for the inner exception:
catch (AggregateException ex)
{
if(ex.InnerException is NullReferenceException)
// handle NRE
else
throw; // NOT "throw ex" to keep the stack trace
}

Try Catch outside of: await Task.Run(()

Does try catch outside of: await Task.Run(() => make sense or just use them only inside of await?
private async void Test()
{
try
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (Exception ex)
{
log.Error(ex.Message);
}
});
}
catch (Exception ex)
{
log.Error(ex.Message);
}
}
If you handle Exception inside the delegate (in your case just for logging purpose), await will not raise an exception in normal circumstances. This should be fine.
private async Task Test()
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (Exception ex)
{
log.Error(ex.Message);
}
});
}
However, since you are awaiting the Task, most probably, there will be some DoSomethingElse in the Test method, which might be affected by the outcome of the Task - in which case it also makes sense to have a try/catch around await.
private async Task Test()
{
try
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (SomeSpecialException spex)
{
// it is OK to have this exception
log.Error(ex.Message);
}
});
DoSomethingElse(); // does not run when unexpected exception occurs.
}
catch (Exception ex)
{
// Here we are also running on captured SynchronizationContext
// So, can update UI to show error ....
}
}
If the delegate you pass to Task.Run raises an exception, then you can catch it outside the Task.Run when you await the returned task.
You shouldn't think of await as though it was a block. There's no such thing as "inside of await". Instead, think of await as an operator that takes a single argument (in this case, the Task returned by Task.Run). Task.Run will catch exceptions from its delegate and place them on the returned Task; await will then propagate that exception.
You can add try catch to outside code too. The compiler will execute catch section when an exception happens during the async call. Here is more details why would you need try catch around await http://msdn.microsoft.com/en-us/library/vstudio/0yd65esw.aspx look Exceptions in Async Methods
This is the behavior in .Net 6:
try{
await Task.Run(()=> & call whatever method );
}
catch { handle the exception } <-- the catch will never get hit. An unhandled exception in the Task.Run line will happen
However, this will work:
try{
await Task.Run(async ()=> & call some method );
}
catch(Exception ex){
handle the exception
}
The async before the ()=> has to be there for .Net 6
I've just confirmed this.

Categories

Resources