Exception from PLinq AsParallel async crashes the app - c#

My question is about capturing exceptions in ForAll method under Plinq
I was trying to run tasks concurently with setting max number of threads.
Using
enumerable
.AsParallel()
.WithDegreeOfParallelism(100)
.ForAll(async item => await AsyncTask())
It works, but if AsyncTask throws exception the app crashes.
I have done the following test:
try
{
IEnumerable<string> enumerable = new List<string> { "st", "st" };
enumerable.AsParallel()
.ForAll(async f =>
{
try
{
throw new Exception(); // Or await AsyncTask that throws this
}
catch (Exception e)
{
e.ToString(); **// This Exception is captured**
throw e;
}
});
}
catch (Exception e) **// THIS IS NOT CAPTURED AND THE APP CRASHES**
{
e.ToString();
}
And I would like to understand the reasons for this
And other options to implement

enumerable.AsParallel().ForAll() executes the given action for each element of your enumeration in parallel. Since your given action is async by itself, ForAll() does not wait until all actions completed. In this case the executed code leaves the try..catch block before your AsyncTask() method throws the exception. This may lead to an unhandled exception, which crashes your app.
It does not matter, that you try to await the AsyncTask(), because ForAll() gets a plain Action and does not await the result of your AsyncTask().
A possible solution could be to start your AsyncTasks for each element without AsParallel().ForEach() and await the results later inside your try..catch.
When storing the
Task or Task<T>
result in a list you can check if any task was throwing an exception using the task.Exception property.
You can do something like this:
private async Task DoSomethingAsync()
{
try
{
IEnumerable<string> enumerable = new List<string> { "st", "st" };
// start all tasks and store them in an array
var tasks = enumerable.Select(TaskAsync).ToArray();
// do something more without waiting until all tasks above completed
// ...
// await all tasks
var completionTask = Task.WhenAll(tasks);
await completionTask;
// handle task exception if any exists
if (completionTask.Status == TaskStatus.Faulted)
{
foreach (var task in tasks)
{
if (task.Exception != null)
{
// throw an exception or handle the exception, e.g. log the exceptions to file / database
}
}
}
}
catch (Exception e)
{
// handle your exception, e.g. write a log to file / database
}
}
private Task TaskAsync(string item)
{
// Task.Delay() is just a placeholder
// do some async stuff here, e.g. access web services or a database
return Task.Delay(10000);
}

Related

Is there a way to Wait for a TPL Task without in throwing an exception?

Some of us prefer to code in an exception-light style. However, if you wait for a Task Parallel Library task, and the task threw an exception, it will throw an exception on the calling thread as well. Is there a (preferably standard) way to avoid this behaviour and just check the response for exceptions when you get it back?
You can use Task.WaitAny like:
var task = Task.Run(() =>
{
// ...
throw new Exception("Blah");
});
Task.WaitAny(task);
if (task.IsFaulted)
{
var error = task.Exception;
// ...
}
else if (task.IsCanceled)
{
// ...
}
else
{
// Success
}
You can't wait on a faulted task without raising an exception.
But you can wait on a continuation to that task, which will complete only after the original task completed without raising an exception:
public static Task SwallowExceptions(this Task task)
{
return task.ContinueWith(_ => { });
}
faultedTask.SwallowExceptions().Wait();
if (faultedTask.IsFaulted)
{
// handle exception
}
If your task returns a value, you can represent that in the extensions method and return the actual value if there were no exceptions or the default value if there were:
public static Task<T> SwallowExceptions<T>(this Task<T> task)
{
return task.ContinueWith(completedTask =>
completedTask.IsFaulted
? default(T)
: completedTask.Result);
}
Unfortunately, this functionality is not built-in. Use this workaround:
myTask.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously).Wait();
You can make this into an extension method.
Based on what you have written, might catching the exception and checking the IsFaulted property be your solution? IsFaulted
Catch the exception within the Task and return it in the result?
var task1 = Task.Factory.StartNew(() =>
{
try
{
throw new MyCustomException("I'm bad, but not too bad!");
}
catch(Exception ex)
{
return new Result { Error = ex };
}
});

await Task.WhenAll(tasks) Exception Handling, log all exceptions from the tasks

I am trying to figure out how to report all exceptions thrown by a list of tasks from the code below.
The basic idea of this code snippet is: The user sends a request to the handler, the handler creates the messages tasks and send them to a class that send them to the external system. I included the methods involved below.
I must have something off because I was debugging the exception handler and the tasks Exception are always null because it seems their status is Waiting for Activiation unless I stay in the breakpoint long enough.
// Handle the user request
public async void Handle(WriteScanToSys settings)
{
_successfulScanIds = new List<int>();
// create the messages as tasks
var tasks = _factory.CreateMessage(settings).Select(msg => SendScans(msg));
try
{
// wait for all of them to complete
await Task.WhenAll(tasks); // I had ConfigureAwait(false) here, but took it off
}
catch (Exception)
{
foreach (var task in tasks.Where(t => t.Exception != null))
{
// ELMAH
var errorLog = ErrorLog.GetDefault(null);
errorLog.Log(new Error(task.Exception));
}
}
// save to repository
}
// the task to perform
private async Task<IDictionary<string, object>> SendScans(IDictionary<string, object> data)
{
object sysMsg = null;
var response = await _msgCenter.SendMessage(data);
response.TryGetValue("SystemMessage", out sysMsg);
_successfulScanIds.Add(Convert.ToInt32(data["Id"]));
return response;
}
// the communication with the external system (The message center class)
private async Task<IDictionary<string, object>> SendMessage(IDictionary<string, object> msg)
{
var response = new Dictionary<string, object>();
var response = await _client.sendAsync(
new BodyOfRequest(
// Compose Object
));
if (response.ScreenMessage != "SUCCESSFUL")
throw new CustomException("The transaction for job " + job + " failed with msg: " + body.ScreenMessage);
response.Add("SystemMessage", body.ScreenMessage);
return response;
}
You've fallen foul of lazy evaluation - the result of Select will create a new set of tasks each time you iterate over it. You can fix this just by calling ToList():
var tasks = _factory.CreateMessage(settings)
.Select(msg => SendScans(msg))
.ToList();
That way the set of tasks that you're awaiting will be the same set of tasks checked with your foreach loop.
Instead of iterating over all tasks, you can get the Exceptions (if any) from the Task.WhenAll-Task:
var taskResult = Task.WhenAll(tasks);
try
{
await taskResult;
}
catch (Exception e)
{
if (taskResult.IsCanceled)
{
// Cancellation is most likely due to a shared cancellation token. Handle as needed, possibly check if ((TaskCanceledException)e).CancellationToken == token etc.
}
else if (taskResult.IsFaulted)
{
// use taskResult.Exception which is an AggregateException - which you can iterate over (it's a tree! .Flatten() might help)
// caught exception is only the first observed exception
}
else
{
// Well, this should not really happen because it would mean: Exception thrown, not faulted nor cancelled but completed
}
}

Gracefully handeling exceptions when dealing with WhenAll

I'm playing with a piece of code I wrote a while back. That piece of code deals with making a few requests in an async manner.
var client = new HttpClient();
var searchPromises = searchTerms
.Select(GetSearchUrl)
.Select(client.GetStringAsync);
var searchPages = await Task.WhenAll(searchPromises);
What happens is I create a new HttpClient. Using some search terch terms I compose search engine urls. Then I use those urls as inputs to get tasks representing the async requests for a page with the results. And last, I await those responses using Task.WhenAll to group them together.
The problem is if just one of those requests gets a 404, a 500 or anything like that my code throws an AggregateException.
Is there a way of specifying what should happen in the case of an error in one of those threads, so that I get a result from everything else?
I've looked at ContinueWith, but it doesn't seem to fit the bill, that is, it doesn't know how to deal with all the errors, just the aggregate one.
What happens is I create a new HttpClient. Using some search terch terms I compose search engine urls. Then I use those urls as inputs to get tasks representing the async requests for a page with the results. And last, I await those responses using Task.WhenAll to group them together.
Is there a way of specifying what should happen in the case of an error in one of those threads, so that I get a result from everything else?
IMO, the easiest solution is to change how you think about the problem. Right now, you're thinking "perform a download on each url" and then "what for them all to complete and handle errors on a per-item basis". Just change your operation ("download") to include anything you want to do per-item. In other words, what you want to do is "perform a download on each url and handle errors" and then "wait for them all to complete":
var client = new HttpClient();
var searchPromises = searchTerms
.Select(GetSearchUrl)
.Select(url => DownloadAsync(client, url));
var searchPages = await Task.WhenAll(searchPromises);
var successfulSearchPages = searchPages.Where(x => x != null);
...
private static async Task<string> DownloadAsync(HttpClient client, string url)
{
try
{
return await client.GetStringAsync(url);
}
catch (HttpRequestException ex)
{
// TODO: Perform appropriate error handling
return null;
}
}
Task.WhenAll will return a task that is completed when all the tasks passed as argument are completed.
If any of the tasks passed as argument ends in a Faulted state (an exception was thrown), the returned task will also end in a Faulted state and its Exception property will contain the aggregation of all exceptions thrown by the tasks passed as argument.
Because the code generated by the compiler picks the first exceptin on the list, only the excpetion thrown by the first exception that throws (not the first exception thrwing) will be rethrown.
But the tasks passed as argument still exist and can still be queried for result.
This code snippet shows this working:
var tasks = new Task[] {
((Func<Task>)(async () =>
{
await Task.Delay(10);
await Task.Delay(10);
await Task.Delay(10);
throw new Exception("First");
}))(),
((Func<Task>)(async () =>
{
await Task.Delay(10);
throw new Exception("Second");
}))(),
((Func<Task>)(async () =>
{
await Task.Delay(10);
}))()
};
var allTasks = Task.WhenAll(tasks);
try
{
await allTasks;
}
catch (Exception ex)
{
Console.WriteLine("Overall failed: {0}", ex.Message);
}
for(var i = 0; i < tasks.Length; i++)
{
try
{
await tasks[i];
Console.WriteLine("Taks {0} succeeded!", i);
}
catch (Exception ex)
{
Console.WriteLine("Taks {0} failed!", i);
}
}
/*
Overall failed: First
Taks 0 failed!
Taks 1 failed!
Taks 2 succeeded!
*/
You can create your own version of Task.WhenAll that returns just the results disregarding any exception using Task.WhenAny:
public static async Task<IEnumerable<TResult>> WhenAllSwallowExceptions<TResult>(IEnumerable<Task<TResult>> tasks)
{
var tasklist = tasks.ToList();
var results = new List<TResult>();
while (tasklist.Any())
{
var completedTask = await Task.WhenAny(tasklist);
try
{
results.Add(await completedTask);
}
catch (Exception e)
{
// handle
}
tasklist.Remove(completedTask);
}
return results;
}
Usage:
var searchPages = await WhenAllSwallowExceptions(searchPromises);
This waits for tasks one at a time (with Task.WhenAny) and aggregates all the results (if there are any).
I've found a way to do this, after many iterations. Tasks are starting to look like things that you need a library to abstract.
Anyway, here's the code:
var client = new HttpClient();
var exceptions = new ConcurrentBag<Exception>();
var searchPromises = searchTerms
.Select(GetSearchUrl)
.Select(client.GetStringAsync)
.Select(t=>t.Catch(e=>exceptions.Add(e)));
var searchPages = (await Task.WhenAll(searchPromises))
.Where(r => r != null);
And the implementation for Catch:
public static Task<TResult> Catch<TResult>(this Task<TResult> self, Action<Exception> exceptionHandlerTask)
{
return self.ContinueWith(s =>
{
if (!s.IsFaulted)
{
return s.Result;
}
exceptionHandlerTask(s.Exception);
return default(TResult);
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DenyChildAttach,
TaskScheduler.Default);
}
What happens now is that it gives you a way to append a failure state function to the Task<T> promise. This allows me to still have chainability. It is a shame that c# doesn't have robust support for functional pattern matching to make this easier.
Edit: added minimal code for error logging.
Edit: separated the code for logging errors to be more generic/reusable.
Edit: separated the code for saving the errors from the Catch function.

Catching exception throwing by tasks

TaskScheduler ts= TaskScheduler.FromCurrentSynchronizationContext();
try
{
Task<T> task1 = ...
task1.ContinueWith(t =>
{
...
Task<T> task2 = ...
task2.ContinueWith(u =>
{
...
Task<T> task3 = ...
task3.ContinueWith(w =>
{
...
}, new CancellationToken(), TaskContinuationOptions.OnlyOnRanToCompletion, ts);
}, new CancellationToken(), TaskContinuationOptions.OnlyOnRanToCompletion, ts);
}, new CancellationToken(), TaskContinuationOptions.OnlyOnRanToCompletion, ts);
}
catch(Exception)
{
MessageBox.Show("...");
}
Hi. I have some code (as above). This doesn't work for me. I have three tasks which are working server-side but modifying UI, so all of them should result make in UI thread. What is more: 3rd task cannot run if 2nd complete with failure, and 2nd cannot run unless 1st is successfully completed. So if 1st one ends with a failure my tasks tree should throw exception and break rest of operations. How to achieve that in the simplest way?
UPDATE
now my code looks like
private async void SomeMethod()
{
...
try
{
var r1 = await Method1(...);
var r2 = await Method2(...);
var r3 = await Method3(...);
}
catch
{
MessageBox.Show("...");
}
}
private Task<...> Method1(...)
{
Task<...> task = Task<...>.Factory.StartNew(() =>
{
...
try
{
// Result is null (but that's ok) so 'index out of range exception' is thrown
// It calls my method MyException with this exception (but I don't know in
// which thread and how to catch this (shouldn't be catch by async SomeMethod?)
result = ....Results[0];
}
catch (Exception ex)
{
MyException(ex);
}
return result;
});
return task;
}
public void MyException(Exception ex)
{
throw ex;
}
But I still cannot catch exception.
EDIT
Solved. I don't catch exceptions (just ignore in Method1) and:
var r1 = await Method1(...);
if(r1!=null)
{
var r2 = await Method2(...);
var r3 = await Method3(...);
}
else
{
...do sth instead of catching ex
}
The easiest option is to use await here, as it will provide the error handling semantics you want with very little effort; allowing you to write code as if it were regular synchronous code:
try
{
var firstResult = await SomethingAsync();
var secondResult = await SomethingElseAsync(firstResult);
var finalResult = await AnotherThingAsync(secondResult);
}
catch
{
//handle an exception thrown by any of the above async operations.
}
If you can't do that (due to being on .NET 4.0), then you can use the Then method described here to also get the semantics you want:
var finalTask = SomethingAsync()
.Then(firstResult => SomethingElseAsync(firstResult))
.Then(secondResult => AnotherThingAsync(secondResult));
finalTask.ContinueWith(t => HandleError(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
Then is essentially a call to ContinueWith, but with different error handling semantics. If the tasks being continued threw an exception then Then just propagates that exception, rather than running the continuation. ContinueWith just runs the continuation and swallows the exception.

Handle exception thrown by a task

I have a task running a long time operation in WPF:
Task t = Task.Factory.StartNew(() =>
{
try
{
process(cancelTokenSource.Token, CompressionMethod, OpInfo);
}
catch (OperationCanceledException)
{
logger.Info("Operation cancelled by the user");
}
}, cancelTokenSource.Token);
try
{
t.Wait();
}
catch (AggregateException ae)
{
int i = 0;
}
private void process(CancellationToken token, CompressionLevel level, OperationInfo info)
{
// check hash
if (ComputeHash)
{
logger.Info("HASH CHECKING NOT IMPLEMENTED YET!");
MessageBox.Show(this,"HASH CHECKING NOT IMPLEMENTED YET!", "WARNING", MessageBoxButton.OK, MessageBoxImage.Warning);
}
token.ThrowIfCancellationRequested();
UserMsgPhase = "Operation finished";
return info;
}
Problem is "MessageBox.Show" throws an exception and it is not captured within "catch (AggregateException ae)". I've been reading about TPL exception handling but I don't understand why it is not catched. Please, could you help me?
Once the task is complete you can check its Exception property. You also have Status and IsCompleted properties which may be useful to you...
Check Task.Exception.
If your task is typed (returning a result), then accessing myTask.Result will throw this exception.
Moreover, if you are running .Net 4.5, you could use async/await.
As an example:
public async void MyButton_OnClick(object sender, EventArgs e)
{
try
{
Task t = ...your task...;
var myResult = await t; // do whatever you like with your task's result (if any)
}catch
{
// whatever you need
}
}
as you would do with synchronous code (but this is not an actual synchronous call)
I believe that the question's process method is a Task, so it looks like it could be implement in a different manner:
You can make the process to be implemented as Task and then you will have a task-child within task-parent.
Then you can make use of the TaskCreationOptions.AttachedToParent option.
According to Stephen Toub, using AttachedToParent will help notify children-task exception to the parent-task catch:
any exceptions from faulted children will propagate up to the parent
Task (unless the parent Task observes those exceptions before it
completes).
Example:
I've omitted the cancellation token parts in order for it to be more simple.
Task t = Task.Factory.StartNew(() =>
{
var process = new Task(() =>
{
//Copy here the process logic.
}, TaskCreationOptions.AttachedToParent);
//*Private failure handler*.
process.start();
});
try
{
t.Wait();
}
catch (AggregateException ae)
{
//handle exceptions from process.
}
In addition, you may add a private failure handler like:
//*Private failure handler*.
var failHandler = child.ContinueWith(t =>
{
//Oops, something went wrong...
}, TaskContinuationOptions.AttachedToParent|TaskContinuationOptions.OnlyOnFaulted);

Categories