我遇到了一个问题,如何正确取消异步任务。是否有正确的方法来取消异步任务?
这是一些草案。
我的入口点运行两个异步任务。第一项任务做了一些“长期”的工作,第二项任务取消了它。
切入点:
private static void Main()
{
var ctc = new CancellationTokenSource();
var cancellable = ExecuteLongCancellableMethod(ctc.Token);
var cancelationTask = Task.Run(() =>
{
Thread.Sleep(2000);
Console.WriteLine("[Before cancellation]");
ctc.Cancel();
});
try
{
Task.WaitAll(cancellable, cancelationTask);
}
catch (Exception e)
{
Console.WriteLine($"An exception occurred with type {e.GetType().Name}");
}
}
方法,返回可取消的任务:
private static Task ExecuteLongCancellableMethod(CancellationToken token)
{
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
Console.WriteLine("1st");
Thread.Sleep(1000);
Console.WriteLine("2nd");
Thread.Sleep(1000);
Console.WriteLine("3rd");
Thread.Sleep(1000);
Console.WriteLine("4th");
Thread.Sleep(1000);
Console.WriteLine("[Completed]");
}, token);
}
我的目的是停止写入 '1', '第二', '3'立即在取消之后被调用。 但我得到以下结果:
1st
2nd
3rd
[Before cancellation]
4th
[Completed]
出于显而易见的原因,我没有得到请求取消时抛出异常。于是,我就重写方法如下:
private static Task ExecuteLongCancellableAdvancedMethod(CancellationToken token)
{
return Task.Run(() =>
{
var actions = new List<Action>
{
() => Console.WriteLine("1st"),
() => Console.WriteLine("2nd"),
() => Console.WriteLine("3rd"),
() => Console.WriteLine("4th"),
() => Console.WriteLine("[Completed]")
};
foreach (var action in actions)
{
token.ThrowIfCancellationRequested();
action.Invoke();
Thread.Sleep(1000);
}
}, token);
}
现在我得到了我想要的:
1st
2nd
[Before cancellation]
3rd
An exception occurred with type AggregateException
,但我想在创建动作代表的集合和循环通过它是不是最方便处理我的问题的方法。
那么,正确的方法是什么?为什么我需要将取消令牌作为第二个参数传递给Task.Run方法?
如果令牌取消了已经申请,'Task.Run'不会跑委托在所有 –
您需要显式调用'token.ThrowIfCancellationRequested();'在您想要的每一个地方执行实际上是可以取消的。所以在你的第一个例子中,在每个'Console.WriteLine'之前。 –
我强烈建议你阅读ContinueWith方法,因为你问到“正确”的方式,我想你会想要熟悉ContinueWith和TaskContinuationOptions.NotOnCanceled。以下是包含更多有用信息的链接:https://social.msdn.microsoft.com/Forums/zh-CN/310782d8-aadf-4841-a309-23abff407d9a/cancellation-using-exception-to-control-application-flow?论坛= parallelextensions – Jace