2

I'm currently creating a demonstration project to show the rest of my team about how they can use TPL to make better code. However, I'm stumped on a single issue that I figure should be functioning another way; the code:

// Example 7 - Even more exceptions try { var numberList = Enumerable.Range(0, 1000).AsParallel().Select(x => { if (x % 2 == 0) throw new ApplicationException("Shazam!"); return x; }).ToList(); } catch (AggregateException e) { int exceptionsAggregated = 0; e.Flatten().Handle(ex => { if (ex is ApplicationException) { if ((ex as ApplicationException).Message == "Shazam!") exceptionsAggregated++; } return true; }); Console.WriteLine("Exceptions: " + exceptionsAggregated); } 

What I would except to happen is that the aggregate exception would contain 500 inner exceptions because each other thread invoked in PLINQ would throw an exception. However, I'm only getting 4 exceptions in the Aggregate exception.

My first though was that perhaps TPL terminates the run when it reached a limit on the number of exceptions that could be thrown. However, I can't seem to find any online articles or documentation that would support that claim. So I'm kinda stumped here; what would caused only 4 exceptions to be contained?

Am I just missing something fundamental here?

EDIT: @Dan Bryant below pegged it on the head; when I change the code to the following:

// Example 7 - Even more exceptions

try { var tasks = Enumerable.Range(0, 100).Select(x => Task.Factory.StartNew(() => { if (x % 2 == 0) throw new ApplicationException("Shazam!"); return x; })).ToArray(); Task.WaitAll(tasks); } catch (AggregateException e) { int exceptionsAggregated = 0; e.Flatten().Handle(ex => { if (ex is ApplicationException) { if ((ex as ApplicationException).Message == "Shazam!") exceptionsAggregated++; } return true; }); Console.WriteLine("Exceptions: " + exceptionsAggregated); } 

I correctly get the right number of exceptions. Problem solved!

2 Answers 2

2

One possibility is that the parallel Select is creating a parent task, with a child task for each iteration. The default TaskScheduler will only execute a certain number of tasks at the same time; if any of these child tasks fail, the parent task will fail, which means child tasks that have not yet been started will not execute.

This is line with the concept that Select should not have side effects, since a failure in the middle of a Select will prevent subsequent enumeration calls to stop executing. The difference with the Parallel version is that you can have a few exceptions occur (due to the partially parallel execution), whereas a 'serial' Select can only throw a single exception as it enumerates.

Another possibility is that it creates a fixed number of tasks, then allocates work to them through concurrent blocking collections. Once each task fails, it stops executing its allocated workload. I think this latter explanation is actually more likely, but I'd have to look into the implementation to know for sure.

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

1 Comment

Interesting. Let's test that out; if I assign a task to each element of the list, and then WaitAll, I should get 100 then...
1

The tasks stop being scheduling tasks once exceptions are found. It returns the exceptions it has up to that point. Existing tasks are allowed to complete (if they can) and you only get the exceptions back for the tasks that actually ran. Any tasks that were still waiting to run don't get started. (Remember, the tasks don't necessarily start immediately)

Here is more information I blogged about a few months ago: http://colinmackay.co.uk/blog/2011/02/14/parallelisation-in-net-40-part-2-throwing-exceptions/

And because you are using PLINQ, you should also be aware that exceptions won't bubble up to the calling thread until you call something that calls WaitAll. More info: http://colinmackay.co.uk/blog/2011/05/16/tasks-that-throw-exceptions/

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.