取消并行

时间:2018-10-10 10:05:25

标签: c# parallel-processing

我有Microsoft documentation的这段代码。但是它没有按预期工作。完成循环后,循环将停止,而无需单击取消键。没有按键或console.write在新任务中显示。

控制台输出,解释了调试跟踪:

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

我期望的是:启动一个等待特定密钥的线程。如果未击中此特定键,则提示输入特定键。

我做了什么:

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代码,对跟踪进行次要编辑,尝试调试。

1 个答案:

答案 0 :(得分:1)

您缺少一行,它在MSDN代码中:

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

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

并且,您的while循环过早退出。您应该删除Console.KeyAvailable行。

还要注意,Console.ReadKey()阻塞了循环。

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

或者,如果您想要相同的流程,则可以使用:

 while (!Console.KeyAvailable)

在这种情况下,取自this msdn example,应该像这样:

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);

注意:Sleep不会使您的CPU过载。


完整的工作代码:(注:没有应用睡眠,并且Press 'c' to cancel的显示方式不是您可以实际看到的,IMO,您应该只保留while (!Console.KeyAvailable)部分。

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();
        }
    }
}