我正在测试在Linqpad上创建的一个简单的控制台应用程序,想法是确保Task
的工作有所了解并创建一个有效的逻辑,当Task
完成,出现故障或取消即可。我想只在Task完成但没有出现故障或取消时才执行逻辑。
void Main()
{
CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0,0,0,0,1000));
Task t = Task.Run(() => Work(),cts.Token);
try
{
t.Wait();
}
catch
{
}
("Completed :: " + t.IsCompleted).Dump();
("Canceled :: " + t.IsCanceled).Dump();
("Faulted :: " + t.IsFaulted).Dump();
}
public async Task Work()
{
await Task.Delay(3000);
}
以下是问题:
我能够自信地找出已完成状态和故障状态,但即使在我看来此代码应该导致任务取消,IsCanceled
属性的值始终为false。
理想情况下,当Task出现故障时,即使我在try catch块中静默捕获异常,它也应该将IsCompleted
显示为false,但它始终保持为真,目前Linqpad并没有这样做。我有继续错误选项,但我假设,如果我可以继续出错,它会变为假
答案 0 :(得分:5)
我能够自信地找出已完成状态和故障状态,但即使在我看来这个代码应该导致任务取消,IsCanceled属性的值总是为假。
取消时没有自动化。您正在将CancellationToken
传递给Task.Run
。如果在任务启动时取消,则取消将中断启动过程。任务运行后,任务的方法是检查取消令牌的责任。 Wait
没有这样做。它甚至不知道取消令牌。因此,任务永远不会变成被取消的状态。
这是您观察取消的方式:
void Main()
{
CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0,0,0,0,1000));
Task t = Task.Run(() => Work(cts.Token),cts.Token);
try
{
t.Wait();
}
catch
{
}
("Completed :: " + t.IsCompleted).Dump();
("Canceled :: " + t.IsCanceled).Dump();
("Faulted :: " + t.IsFaulted).Dump();
}
public async Task Work(CancellationToken token)
{
await Task.Delay(3000, token);
}
理想情况下,当Task出现故障时,即使我在try catch块中静默捕获异常,它也应该将IsCompleted显示为false,但它始终保持为真
检查MSDN:
当任务处于以下三种最终状态之一时,IsCompleted将返回true:RanToCompletion,Faulted或Cancelled。
答案 1 :(得分:5)
其他人已经注意到您的代码没有遵守CancellationToken
,这就是为什么任务没有被取消。
我会回答这部分问题:
我想只在任务完成但没有出现故障或取消时才执行逻辑。
为此,请在await
任务之后输入逻辑:
await t;
// Your logic here.
使用IsCanceled
/ IsFaulted
/ IsCompleted
来控制流量是代码气味。
答案 2 :(得分:2)
您没有将CancellationToken
传递给Task.Delay
方法,因此无需取消任何内容。
您在Task.Run(xxx)
中传递的令牌可以防止在令牌未完成取消时启动工作。但是,您的令牌会在1秒钟后被取消,这是在致电Task.Run
后很长时间。
试试这个:
void Main()
{
CancellationTokenSource cts = new CancellationTokenSource(new TimeSpan(0, 0, 0, 0, 1000));
Task t = Task.Run(() => Work(cts.Token), cts.Token);
try
{
t.Wait();
}
catch
{
}
("Completed :: " + t.IsCompleted).Dump();
("Canceled :: " + t.IsCanceled).Dump();
("Faulted :: " + t.IsFaulted).Dump();
}
public async Task Work(CancellationToken t)
{
await Task.Delay(3000, t);
}