我遇到了一個問題,如何正確取消異步任務。是否有正確的方法來取消異步任務?
這是一些草案。
我的入口點運行兩個異步任務。第一項任務做了一些「長期」的工作,第二項任務取消了它。
切入點:
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