我有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代码,对跟踪进行次要编辑,尝试调试。
答案 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();
}
}
}