我有一个简短的异步任务,在启动后经常需要取消。 “Task”类有一个IsCanceled指示器,我认为可以方便地指示异步任务在没有运行完成的情况下被取消,但据我所知,将异步任务标记为已取消的唯一方法是在异步函数中抛出TaskCanceledException。例行地抛出异常,以指示无异常发生的情况,违背了我应该如何理解应该使用的异常。是否有人知道更好的方法来指示异常任务在预计经常发生时被取消?
我的下一个最佳选择是返回一个拥有它自己的IsCanceled属性的结构:
(为了简洁,我忽略了一些好的编码和样式练习)
class MightBeCanceled<T>
{
public readonly T Value;
public readonly bool IsCanceled;
public MightBeCanceled(T value) { Value = value; IsCanceled = false; }
public static MightBeCanceled<T> Canceled = new MightBeCanceled<T>(default(T), true);
private MightBeCanceled(T value, bool isCanceled) { Value = value; IsCanceled = isCanceled; }
}
...
static async Task<MightBeCanceled<int>> Foo()
{
if (someCancellationCondition)
return MightBeCanceled<int>.Canceled;
else
return new MightBeCanceled<int>(42);
}
static async void Bar()
{
var mightBeCanceled = await Foo();
if (mightBeCanceled.IsCanceled)
; // Take canceled action
else
; // Take normal action
}
但这似乎多余且难以使用。更不用说它引入了一致性问题,因为会有两个IsCanceled(一个在Task中,一个在MightBeCanceled中)。
答案 0 :(得分:2)
问题是在您选择等待之前,将无法观察到异常。因此,调用await
的行为意味着在最终返回最终值或等待任务完成时,您最终会在某个时刻观察异常。但是,如果你从不关心(这是一个火灾和忘记任务),那么例外是一个非问题(大多数情况下)。
我也是,发现这有点奇怪,总是不得不尝试/捕获任务来处理聚合异常。但是,请考虑一下:
try
{
var myResult = await Foo();
// Do Success Actions Here...
}
catch(AggregateException e)
{
e.Flatten().Handle(ex =>
{
if(ex is OperationCanceledException)
{
// Do Canceled Thing Here
return true;
}
return false;
});
}
这不是太远了。在很多方面,我想取消另一项任务,我该怎么做? ThreadAbortException
?到目前为止,似乎只是在取消时抛出一个特定的例外。
这几乎是我在多个地方提出的关于如何处理取消的模式。
答案 1 :(得分:2)
通常,取消的目的是让异步操作的调用者告诉操作它应该停止处理。为此,您将CancellationToken
传递给异步方法。这就是等待设置IsCancelled的任务的原因。这意味着外部挑起行动的特殊信号。任务的取消不应该用于控制流程,而只是为了给异步操作提供一个选项,如果等待方已经发出信号表明它不再需要结果,则可以提前完成。
如果您在内部取消了异步操作,那么您已经重载了这个概念,我会说反映这种取消差异是值得的,并且应该是结果容器中的属性,类似于您提出的方式。但不是称之为MightBeCancelled<T>
,而是像InternallyCancellableResult<T>
那样,以反映取消概念的不同。