4

I have the following code :

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;

Task.Factory.StartNew(() =>
{
     if (Console.ReadKey().KeyChar == 'c')
         cts.Cancel();
     Console.WriteLine("press any key to exit");
});

 Parallel.ForEach(list, po, (algo) =>
 {
      algo.Compute(); // this compute lasts 1 minute  
      Console.WriteLine("this job is finished");       
      po.CancellationToken.ThrowIfCancellationRequested();
 });

The list contains few elements. All the Compute methods have already been started when I press 'c'.

When I press 'c', no exception is thrown. Each Compute methods continues its execution until its normal end.

I would like to stop/kill all the remain Compute methods when I press 'c'.

noseratio
  • 59,932
  • 34
  • 208
  • 486
Patrice Pezillier
  • 4,476
  • 9
  • 40
  • 50
  • 3
    I think you need to handle the cancelation in the `Unit-Of-Work` method manually` Once the thread is started, it is on it`s own. You need to throw this from within the thread AFAIK. – Elad Lachmi Mar 20 '14 at 11:00
  • 2
    Yep, you have to pass the cancellation token to `algo.Compute()` and check it within that method's loop. – Matthew Watson Mar 20 '14 at 11:20
  • possible duplicate of [How do I abort/cancel TPL Tasks?](http://stackoverflow.com/questions/4783865/how-do-i-abort-cancel-tpl-tasks) – Fedor Mar 20 '14 at 11:25
  • Just by placing the magic `ThrowIfCancellationRequested` line somewhere in the source code does not make anything happen. That line needs to be executed as well. – usr Dec 14 '15 at 15:55

2 Answers2

3

Cancellation doesn't work like that. It's not like calling Thread.Abort() to terminate thread immediately.

For each element in a sequence your code does:

  1. Calls Compute() method
  2. Waits until its completion
  3. Writes to console about finish
  4. Checks if cancellation was requested and throw the OperationCanceledException if it was.

In order to cancel some task you need to pass the CancellationToken to the called method.
Perhaps, it's worth organizing your long running computations as a cycle and check if cancellation were requested at each step in order to stop it ASAP.

For example, in your Compute() method you could perform check like this:

private void Compute(CancellationToken ct)
{
    while (true)
    {
       ComputeNextStep();
       ct.ThrowIfCancellationRequested();
    }
}
Cros
  • 4,367
  • 9
  • 41
  • 47
Fedor
  • 1,548
  • 3
  • 28
  • 38
  • 1
    I`ll add to this that the point of the `CancellationToken` is to be the connecting thread (pun intended) between the process which seeded the new thread and the thread itself. If the seeding process could cancel the thread, there would be no need for a cancelation token, would there? :) So the `CancellationToken` is for signalling. You need to handle the signal. – Elad Lachmi Mar 20 '14 at 11:28
  • 2
    Oh and one more thing... This is also a good design, since before being cancled, your code might want to dispose of some objects or handles. Just "killing" the thread from outside can create memory / resource issues. Better to let the thread handle the cancelation. – Elad Lachmi Mar 20 '14 at 11:29
0

Observe the cancellation with po.CancellationToken.IsCancellationRequested and use ParallelLoopState.Stop to stop Parallel.ForEach:

void Compute(CancellationToken token, ParallelLoopState loopState)
{
    bool more = true;
    while (more)
    {
        if (token.IsCancellationRequested)
        {
            // stop Parallel.ForEach ASAP
            loopState.Stop();
            return;
        }
        // do the calc step
    }
}

// ... 

CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;

Task.Factory.StartNew(() =>
{
    if (Console.ReadKey().KeyChar == 'c')
        cts.Cancel();
    Console.WriteLine("press any key to exit");
});

Parallel.ForEach(list, po, (algo, loopState) =>
{
    algo.Compute(po.CancellationToken, loopState); // this compute lasts 1 minute  
    Console.WriteLine("this job is finished");
});
// observe the cancellation again and throw after Parallel.ForEach
po.CancellationToken.ThrowIfCancellationRequested();
noseratio
  • 59,932
  • 34
  • 208
  • 486