有没有适当的方法来取消异步任务?
Is there a proper way to cancel an asynchronous task?
我遇到了如何正确取消异步任务的问题。
这是一些草稿。
我的入口点运行两个异步任务。第一个任务做一些 'long' 工作,第二个任务取消它。
入口点:
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}");
}
}
returns可取消任务的方法:
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);
}
我的目的是在调用取消后立即停止写入“1st”、“2nd”、“3rd”。 但我得到以下结果:
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
但我想创建一个 Action 委托集合并循环遍历它并不是处理我的问题的最方便的方法。
那么正确的做法是什么呢?为什么我需要将我的取消令牌作为第二个参数传递给 Task.Run 方法?
Task
不会自行取消,由您自行检测取消请求并彻底中止您的工作。这就是 token.ThrowIfCancellationRequested();
所做的。
您应该在整个代码中放置这些检查,在可以完全停止执行或回滚到安全状态的地方。
在你的第二个例子中,你在每次循环迭代中调用它一次,它工作正常。第一个例子只在一开始调用它一次。如果此时令牌尚未取消,任务将 运行 完成,就像您看到的那样。
如果将其更改为如下所示,您也会看到预期的结果。
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
Console.WriteLine("1st");
Thread.Sleep(1000);
token.ThrowIfCancellationRequested();
Console.WriteLine("2nd");
Thread.Sleep(1000);
token.ThrowIfCancellationRequested();
Console.WriteLine("3rd");
Thread.Sleep(1000);
token.ThrowIfCancellationRequested();
Console.WriteLine("4th");
Thread.Sleep(1000);
Console.WriteLine("[Completed]");
}, token);
我遇到了如何正确取消异步任务的问题。
这是一些草稿。
我的入口点运行两个异步任务。第一个任务做一些 'long' 工作,第二个任务取消它。
入口点:
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}");
}
}
returns可取消任务的方法:
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);
}
我的目的是在调用取消后立即停止写入“1st”、“2nd”、“3rd”。 但我得到以下结果:
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
但我想创建一个 Action 委托集合并循环遍历它并不是处理我的问题的最方便的方法。
那么正确的做法是什么呢?为什么我需要将我的取消令牌作为第二个参数传递给 Task.Run 方法?
Task
不会自行取消,由您自行检测取消请求并彻底中止您的工作。这就是 token.ThrowIfCancellationRequested();
所做的。
您应该在整个代码中放置这些检查,在可以完全停止执行或回滚到安全状态的地方。
在你的第二个例子中,你在每次循环迭代中调用它一次,它工作正常。第一个例子只在一开始调用它一次。如果此时令牌尚未取消,任务将 运行 完成,就像您看到的那样。
如果将其更改为如下所示,您也会看到预期的结果。
return Task.Run(() =>
{
token.ThrowIfCancellationRequested();
Console.WriteLine("1st");
Thread.Sleep(1000);
token.ThrowIfCancellationRequested();
Console.WriteLine("2nd");
Thread.Sleep(1000);
token.ThrowIfCancellationRequested();
Console.WriteLine("3rd");
Thread.Sleep(1000);
token.ThrowIfCancellationRequested();
Console.WriteLine("4th");
Thread.Sleep(1000);
Console.WriteLine("[Completed]");
}, token);