xdtTransform
xdtTransform

Reputation: 2057

Canceling a Parallel.ForEach

From Microsoft documentation, I have this piece of code. But it doesn't work as intended. The loop stops without hiting the cancellation key when it's finish. No key press or console.write in the new task is displayed.

Console ouput, debug trace explainned:

Press any key to start. Press 'c' to cancel.    <-- Starting
.                                               <-- Key pressed

<Multiple line of result>                       <-- Parallel.ForEach ouput
(...)
</Multiple line of result>

Canceled                                        <-- Hit finally Parallel.ForEach over
END! Press a key                                <-- Program

What I expected was: Starting a thread waiting for a specific key. If anything else than this specific key is hit, prompt for specific.

What I did:

static void Main(string[] args)
{
    test();
}
static void test()
{
    int[] nums = Enumerable.Range(0, 1000).ToArray();
    CancellationTokenSource cts = new CancellationTokenSource();

    // Use ParallelOptions instance to store the CancellationToken
    ParallelOptions po = new ParallelOptions();
    po.CancellationToken = cts.Token;
    po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
    po.CancellationToken.ThrowIfCancellationRequested();
    Console.WriteLine("Press any key to start. Press 'c' to cancel.");
    Console.ReadKey();

    // Run a task so that we can cancel from another thread.
    Task.Factory.StartNew(() =>
    {
        while (Console.KeyAvailable)
        {
            if (Console.ReadKey().KeyChar == 'c')
            {
                Console.WriteLine("Cancellation..");
                cts.Cancel();
            }
            Console.WriteLine("\nPress 'c' to cancel.");
        }
        Console.WriteLine("task ..");
    });

    try
    {
        Parallel.ForEach(nums, po, (num) =>
        {
            double d = Math.Sqrt(num);
            Console.WriteLine("{0} on {1}", d, Thread.CurrentThread.ManagedThreadId);

        });
    }
    catch (OperationCanceledException e)
    {
        Console.WriteLine(e.Message);
    }
    catch (Exception e)
    {  // perhaps an exception that was not expected? 
        Console.WriteLine(e.Message);
    }
    finally
    {
        cts.Dispose();
        Console.WriteLine("Canceled");
    }

    Console.WriteLine("END! Press a key");
    Console.ReadKey();
}

99% msdn code, minor edit for trace, trying to debug.

Upvotes: 1

Views: 257

Answers (1)

Stefan
Stefan

Reputation: 17658

You are missing a in line, it's in the MSDN code:

Parallel.ForEach(nums, po, (num) =>
{

     //add this one, it is to cancel it.
     //UPDATE: this isn't actually needed
     //po.CancellationToken.ThrowIfCancellationRequested();
 });

And, your while loop exits prematurely. You should remove the Console.KeyAvailable line.

Also note that the Console.ReadKey() is blocking the loop.

Task.Factory.StartNew(() =>
{
      Console.WriteLine("\nPress 'c' to cancel.");
       if (Console.ReadKey().KeyChar == 'c')
       {
             Console.WriteLine("Cancellation..");
             cts.Cancel();
       }
 });

As alternative, if you want the same flow, you could use:

 while (!Console.KeyAvailable)

In that case, taken from this msdn example, it should be like:

while (Console.KeyAvailable == false)
     Thread.Sleep(250); // Loop until input is entered.

cki = Console.ReadKey(true);
Console.WriteLine("You pressed the '{0}' key.", cki.Key);

Note: the Sleep is there to not overload your CPU.


Full working code: (note: no sleep applied and the Press 'c' to cancel isn't displayed in in such a way that you can actually see it, IMO you should just leave out the while (!Console.KeyAvailable) part.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp15
{
    class Program
    {
        static void Main(string[] args)
        {
            test();
        }
        static void test()
        {
            int[] nums = Enumerable.Range(0, 10000).ToArray();
            CancellationTokenSource cts = new CancellationTokenSource();

            // Use ParallelOptions instance to store the CancellationToken
            ParallelOptions po = new ParallelOptions();
            po.CancellationToken = cts.Token;
            po.MaxDegreeOfParallelism = System.Environment.ProcessorCount;
            po.CancellationToken.ThrowIfCancellationRequested();
            Console.WriteLine("Press any key to start. Press 'c' to cancel.");
            Console.ReadKey();

            // Run a task so that we can cancel from another thread.
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    while (!Console.KeyAvailable)
                    {
                        Console.WriteLine("\nPress 'c' to cancel.");
                    }

                    if (Console.ReadKey().KeyChar == 'c')
                    {
                        Console.WriteLine("Cancellation..");
                        cts.Cancel();
                        break;
                    }
                }

                Console.WriteLine("task ..");
            });

            try
            {
                Parallel.ForEach(nums, po, (num) =>
                {
                    double d = Math.Sqrt(num);
                    Console.WriteLine("{0} on {1}", d, Thread.CurrentThread.ManagedThreadId);

                    po.CancellationToken.ThrowIfCancellationRequested();

                });
            }
            catch (OperationCanceledException e)
            {
                Console.WriteLine(e.Message);
            }
            catch (Exception e)
            {  // perhaps an exception that was not expected? 
                Console.WriteLine(e.Message);
            }
            finally
            {
                cts.Dispose();
                Console.WriteLine("Canceled");
            }

            Console.WriteLine("END! Press a key");
            Console.ReadKey();
        }
    }
}

Upvotes: 1

Related Questions