我希望以下代码能够打印“已取消”:
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => Proc(cts.Token), cts.Token);
task.ContinueWith(t => Console.WriteLine("Completed"), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => Console.WriteLine("Cancelled"), TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => Console.WriteLine("Faulted"), TaskContinuationOptions.OnlyOnFaulted);
Thread.Sleep(1000);
cts.Cancel();
Console.ReadKey();
}
static void Proc(CancellationToken token)
{
// do some CPU-intensive work
token.ThrowIfCancellationRequested();
Task.Delay(2000, token).Wait();
// do some CPU-intensive work
token.ThrowIfCancellationRequested();
}
它打印出“Faulted”,提出了这些问题:
答案 0 :(得分:2)
您确实使用此代码滥用Task.Delay()
作为Thread.Sleep()
的可取消版本:
Task.Delay(2000, token).Wait();
它会阻止您使用Task.Factory.StartNew
开始的池线程,以模拟Thread.Sleep()
。否则,该线程可以为其他任务做一些有用的工作。
您的Main
可以简单地重写为:
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var task = Task.Delay(2000, cts.Token);
task.ContinueWith(t => Console.WriteLine("Completed"), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => Console.WriteLine("Cancelled"), TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => Console.WriteLine("Faulted"), TaskContinuationOptions.OnlyOnFaulted);
Thread.Sleep(1000);
cts.Cancel();
Console.ReadKey();
}
如果您确实需要一个单独的线程用于某些CPU绑定工作,您仍然可以在其中使用Task.Delay
而不会阻塞该线程。循环将在await Task.Delay(...)
之后在另一个池线程上继续。例如:
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var task = Task.Run(async () =>
{
var i = 0;
while (true)
{
token.ThrowIfCancellationRequested();
Console.WriteLine(CalcPrimeNumber(i++));
await Task.Delay(200, token);
}
}, token);
task.ContinueWith(t => Console.WriteLine("Completed"), TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => Console.WriteLine("Cancelled"), TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t => Console.WriteLine("Faulted"), TaskContinuationOptions.OnlyOnFaulted);
Thread.Sleep(1000);
cts.Cancel();
Console.ReadKey();
}
使用TPL和async/await
,除了测试之外,您根本不需要使用Thread.Sleep
。
答案 1 :(得分:0)
以下是我能给出的最佳答案。
它打印“Faulted”,因为Task.Delay(...)正在创建一个新的Task。当该子任务被取消时,它会以AggregateException而不是OperationCanceledException传播。
Task.Delay(...)可以用作Thread.Sleep(...)的可取消版本
以下修改按预期工作:
修饰:
static void Proc(CancellationToken token)
{
token.ThrowIfCancellationRequested();
try
{
Task.Delay(2000, token).Wait();
}
catch
{
token.ThrowIfCancellationRequested();
throw;
}
token.ThrowIfCancellationRequested();
}