1

Created a simple program using Linqpad, where I am throwing an exception explicitly in the Parallel Foreach loop, which ideally shall be caught in the caller as Aggregate Exception, but when I explicitly throw the exception, it sometimes skip out few exceptions on random basis. I am not able to understand the behavior, anyone who can explain:

void Main() { try { var intList = new List<int> {1,2,3,4,5,6}; Parallel.ForEach(intList, i => Test1(i)); } catch (AggregateException aggregateException) { foreach (var ex in aggregateException.Flatten().InnerExceptions) { ex.Message.Dump(); } } } public void Test1(int i) { try { if (i % 2 != 0) throw new Exception($"{i} - Odd value exception"); } catch(Exception ex) { ex.Message.Dump(); throw; } } public void Test2(int i) { if (i % 2 != 0) throw new Exception($"{i} - Odd value exception"); } public void Test3(int i) { try { if (i % 2 != 0) throw new Exception($"{i} - Odd value exception"); } catch(Exception ex) { ex.Message.Dump(); } } 

Details:

  1. There two versions of Test, one with explicit Try Catch and other without
  2. Both have similar inconsistent behavior to the extent that in Test1, even local try catch doesn't print the value
  3. There can be third version Test3 which always work as exception is not explicitly thrown out of the parallel loop
  4. Dump is a linqpad print call replace it by Console.WriteLine on the visual studio

There's an option define here, which collects all exceptions in a ConcurrentQueue and throw them later as aggregated exception, but why the current code doesn't work as expected, I am not very sure. In this case we expect Output to be:

1 - Odd value exception 3 - Odd value exception 5 - Odd value exception 

but some of them are randomly skipped, that too in a simple program, there are much more miss in a complex program, which do far more work

3
  • @MickyD No i don't , this is purely for learning, otherwise pattern in the MSDN link is good Commented Sep 13, 2018 at 8:57
  • Excellent. Thanks Commented Sep 13, 2018 at 9:04
  • Parallel.For/Foreach is meant for data parallelism. It creates roughly as many worker tasks as there are cores, partitions the data and feeds each partition to a task. It doesn't make sense of computation to proceed if any of those tasks throws. Commented Sep 13, 2018 at 11:06

1 Answer 1

3

This is entirely expected behaviour.

See the docs,

an unhandled exception causes the loop to terminate immediately

When you throw an exception, no new Tasks will be scheduled.

So the behaviour will appear unpredictable. You have no right to expect that all subtasks will execute. That is not the contract of a Parallel.For loop.

The difference will be much clearer when you add more items to the source list. The output will always show a number of exceptions in the neighbourhood of ThreadPool.MinThreads.

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

5 Comments

You mean the reason for non printing is Parallel loop itself exiting, which then shall not be the case for explicit Tasks
I don't know what you mean with 'explicit Tasks' but I think yes. See the link in my edit.
Task as in TPL Task, which are not same as Parallel loop and are surely executed
@MrinalKamboj Parallel.Foreach uses tasks as workers. It partitions the data and feeds each partition to a different worker. It's meant for data parallelism which is why it uses roughly as many workers as there are cores to ensure the CPU is kept fully occupied. On a 4-core machine it would create 4 tasks to process 800K items, passing 200K of them to each worker. If it didn't the CPU would waste all its time switching from one task to another instead of processing

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.