title: How does Parallel.ForEach handles cancellation or ThrowIfCancellationRequested() and exceptions
body: <p>I created a WPF app to see how TPL works and I'm confused by the output I had. Below is my code:</p>
  
  <pre><code>// Two buttons, 'Process' button and 'Cancel' button
  public partial class MainWindow : Window 
  {
     private CancellationTokenSource cancelToken = new CancellationTokenSource();
     public MainWindow()
     {
        InitializeComponent();
     }
     //...
  
     private void cmdProcess_Click(object sender, EventArgs e)  // Sequence A
     {
        Task.Factory.StartNew(() =&gt; ProcessFiles()); 
     }
  
      private void cmdCancel_Click(object sender, EventArgs e)   //Sequence B
     {
        cancelToken.Cancel();  
     }
  
     private void ProcessFiles() 
     {
        ParallelOptions parOpts = new ParallelOptions();
        parOpts.CancellationToken = cancelToken.Token;
        parOpts.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
  
        string[] files = { "first", "second" };
        try
        {
           Parallel.ForEach(files, parOpts, currentFile =&gt;
           {
              parOpts.CancellationToken.ThrowIfCancellationRequested();  //Sequence C
              Thread.Sleep(5000);
           });
        }
        catch (OperationCanceledException ex)
        { 
           MessageBox.Show("Caught");
        }
     }
  
  }
  </code></pre>
  
  <p>When I press the <code>click</code> button and then press the <code>cancel</code> button quickly, I get a "Caught" message box popping up only once, not twice.</p>
  
  <p>Let's say the primary thread id is 1, and worker threads are 2 and 3
  so I have two questions:</p>
  
  <p>Q1- when I press the <code>cancel</code> button, worker threads 2 and 3 have already executed 'parOpts.CancellationToken.ThrowIfCancellationRequested();' (of course my mouse click cannot be as fast as a thread's execution). When they execute ThrowIfCancellationRequested, the cancelToken hasn't been cancelled, which means that the cancel button has not been clicked for thread 2 and thread 3. So how come those worker threads still throw an exception?</p>
  
  <p>Q2- why do I only get one popup message box, shouldn't it be two, one for thread 2 and one for thread 3?  </p>
  
  <p>Q3- I modified the Parallel.ForEach as:</p>
  
  <pre><code>try
  {
     Parallel.ForEach(files, parOpts, currentFile =&gt;
     {
        Thread.Sleep(5000);
        parOpts.CancellationToken.ThrowIfCancellationRequested(); 
  
     });
  }
  catch (OperationCanceledException ex)
  { 
     MessageBox.Show("Caught");
  }
  </code></pre>
  
  <p>now I can press cancel button before worker thread reaches to ThrowIfCancellationRequested(), but I still get only one exception thrown by the primary thread. BUt I pressed the cancal button, token has been set to cancel, so when the secondary worker thread reaches to  <code>parOpts.CancellationToken.ThrowIfCancellationRequested();</code>, shouldn't it throw an exception too? and this exception cannot be handled by the try catch in the primary thread(each thread has its own stack), so I should get an unhandled exception to halt the application, but it wasn't, I just get one exception thrown by primary thread, and is this exception thrown by primary thread or worker thread?</p>
  
  <p>Q4-I modify the code as:</p>
  
  <pre><code>private void ProcessFilesz()
  {
      ParallelOptions parOpts = new ParallelOptions();
      parOpts.CancellationToken = cancelToken.Token;
      parOpts.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
  
      cancelToken.Cancel(); // cancel here
      string[] files = { "first", "second" };
      try
      {
          Parallel.ForEach(files, parOpts, currentFile =&gt;
          {
              MessageBox.Show("Underline Thread is " + Thread.CurrentThread.ManagedThreadId.ToString());
              parOpts.CancellationToken.ThrowIfCancellationRequested();
          });
  
      }
      catch (OperationCanceledException ex)
      {
          MessageBox.Show("catch");
      }
  }
  </code></pre>
  
  <p>again there is sth weird, there is no messagebox popup, even though the token is set to cancel, but <code>MessageBox.Show(...)</code> statement is above the statement of <code>parOpts.CancellationToken.ThrowIfCancellationRequested();</code>, so the <code>MessageBox.Show()</code> should be executed first, but why it is not executed at all? or CLR hoists <code>parOpts.CancellationToken.ThrowIfCancellationRequested();</code> to the top to be first statement implicitly?</p>
  
  <p>Q5- I modified the code as:</p>
  
  <pre><code>try
  {
     Parallel.ForEach(files, parOpts, currentFile =&gt;
     {
        Thread.Sleep(5000); // I pressed the cancel button on the main thread when the worker thread is sleeping
     });
  }
  catch (OperationCanceledException ex)
  {
     MessageBox.Show("Caught");
  }
  </code></pre>
  
  <p>so I have enough time to press the cancel button, there is one "catch" message, but why there is still an exception? Now I understand that Parallel.ForEach checks CancellationToken.IsCancellationRequested` before all resource expensive operations, does it mean that Parallel.ForEach will check IsCancellationRequested after all statements inside have been executed? I mean Parallel.ForEach will check IsCancellationRequested twice, one before first statement, and one after the last statement?</p>