7

I am trying to allow cancellation of a Parallel.ForEach loop. According to this MSDN article, it is possible, and I am following their coding.

// Tokens for cancellation 
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;

try
{
    Parallel.ForEach(queries, po, (currentQuery) =>
    {
        // Execute query
        ExecuteQuery(currentQuery);

        // Throw exception if cancelled 
        po.CancellationToken.ThrowIfCancellationRequested(); // ***
    }); 
}
catch (OperationCanceledException cancelException)
{
    Console.WriteLine(cancelException.Message);
}

However, when I call cts.Cancel(); from a user-accessible function, the app crashes on the line marked with asterisks above with the error:

System.OperationCanceledException was unhandled by user code
  Message=The operation was canceled.
  Source=mscorlib
  StackTrace:
   at System.Threading.CancellationToken.ThrowIfCancellationRequested()
   at CraigslistReader.SearchObject.<>c__DisplayClass7.<bw_DoWork>b__5(Query currentQuery) in {PATH}:line 286
   at System.Threading.Tasks.Parallel.<>c__DisplayClass2d`2.<ForEachWorker>b__23(Int32 i)
   at System.Threading.Tasks.Parallel.<>c__DisplayClassf`1.<ForWorker>b__c()
InnerException: 

I have the Exception handler right there, so I don't understand the crash. Any ideas?

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Doug
  • 5,116
  • 10
  • 33
  • 42
  • I don't see the problem you're describing, the `catch` works fine for me. Could you post a complete but short code that shows your problem? – svick Sep 24 '12 at 06:45
  • I don't think the app has crashed, at least not with this exception. Either your app has not crashed (how do you know it has crashed?), or this is not the last exception that is occurring. – usr Sep 24 '12 at 09:34
  • @usr Yes, you were right. I was running it in the debugger and when I saw the exception halt the runtime, I figured it was an exception that would crash the app. I didn't know some exceptions aren't crashing ones. – Doug Sep 25 '12 at 01:41
  • I believe this is commonly referred to as a First Chance Exception. As you note, it doesn't mean that it will crash the app, but is just a debugging concept. – BlakeH Feb 28 '22 at 21:32

1 Answers1

2

The issue is that po.CancellationToken.ThrowIfCancellationRequested(); is explicitly throwing an exception, which is unhandled. The exception handler may be around the Parrallel.ForEach() call but the exception is not handled within the lambda expression. Either remove the line or add an exception handler within the lambda expression and it should work.

See Cancelling a Task is throwing an exception for more information.

Community
  • 1
  • 1
akton
  • 14,148
  • 3
  • 43
  • 47
  • @svick Possibly. Have a look at the answer I linked and try out what it suggests. If it works, it works. – akton Sep 24 '12 at 00:49
  • @akton: I tried putting the try/catch inside the lamba expression and it still results in a runtime error. – Doug Sep 24 '12 at 04:19
  • @Doug Hmmm. I'll keep digging and let you know if I find anything. Giving question a bump to see if anyone else knows – akton Sep 24 '12 at 04:27
  • @akton I was able to resolve this by using a try/catch inside the lambda expression AND wrapping the Parallel.ForEach in a try/catch as well. No idea why I had to do both. – Doug Sep 24 '12 at 13:00
  • @Doug Glad that you worked it out for including the answer here. – akton Sep 24 '12 at 13:02
  • What is the point of throwing the exception and immediately catching it? The cancellation exception is meant to bubble up (and yes, it *does* work with Parallel.For). You you don't want an exception poll the property IsCancellationRequested. – usr Sep 24 '12 at 18:49
  • After making some other code changes, I went back today and tried to put a try/catch around the Parallel.ForEach only. This no longer raised an exception in the debugger. Don't know why it had done this before. I then put a try/catch in the lambda expression, and that did surface an exception in the debugger, but did not produce a runtime error. The long story short is: Put the try/catch wherever you want. Both will effectively shut down the Parallel.ForEach loop, and neither results in a crash. – Doug Sep 25 '12 at 01:44
  • I experience the same thing. So how to properly handle it? – Syaiful Nizam Yahya Feb 02 '14 at 13:48
  • @publicENEMY Either catch the exception in the lambda function or do not call `ThrowIfCancellationRequested()`. Throwing an exception is how that method works. – akton Feb 02 '14 at 22:17
  • @akton I kept seeing "catch the exception in the lambda function". How do I actually do that? Are you saying that I should use try/catch block on ThrowIfCancellationRequested() sentence? – Syaiful Nizam Yahya Feb 03 '14 at 03:56
  • 1
    @publicENEMY Put a try/catch around the call to `ThrowIfCancellationRequested()`. – akton Feb 03 '14 at 04:32