在我们的应用程序中,我们使用async/await和Tasks工作了很多.因此它确实使用Task.Run很多,有时使用内置的取消支持CancellationToken
.
public Task DoSomethingAsync(CancellationToken cancellationToken) { return Task.Run(() => { while (true) { if (cancellationToken.IsCancellationRequested) break; //do some work } }, cancellationToken); }
如果我现在使用CancellationToken取消执行,则执行会在下一个循环开始时停止,或者如果Task根本没有启动,则会抛出异常(Task.Run中的TaskCanceledException).现在的问题是为什么Task.Run使用Exception控制成功取消而不是仅返回已完成的Task.是否有任何具体原因MS没有坚持"不要使用例外来控制执行流程"规则?
如何在一个完全无用的try catch(TaskCancelledException)块中避免使用Boxing支持取消(这很多)的每个方法?
好吧,你不能真正看到你的非常简单的情况 - 你实际上并没有使用结果Task
,你不需要通过复杂的调用堆栈传播取消.
首先,您Task
可能会返回一个值.操作取消后您返回什么?
其次,可能还有其他任务跟随您取消的任务.您可能希望在方便时通过其他任务传播取消.
例外传播.任务取消与Thread.Abort
此用法完全相同- 当您发出a时Thread.Abort
,a ThreadAbortException
用于确保您一直放松到顶部.否则,你的所有方法都必须检查他们调用的每个方法的结果,检查它们是否被取消,并在需要时自行返回 - 我们已经看到人们将忽略老派C中的错误返回值:)
最后,任务取消,就像线程中止一样,是一种特殊情况.它已经涉及同步,堆栈展开等.
但是,这并不意味着您必须使用try-catch
捕获异常 - 您可以使用任务状态.例如,您可以使用这样的辅助函数:
public static TaskDefaultIfCanceled (this Task @this, T defaultValue = default(T)) { return @this.ContinueWith ( t => { if (t.IsCanceled) return defaultValue; return t.Result; } ); }
你可以用作哪个
await SomeAsync().DefaultIfCanceled();
当然,应该注意的是,noöne强迫您使用这种取消方法 - 它只是为了方便而提供.例如,您可以使用自己的放大类型来保留取消信息,并手动处理取消.但是当你开始这样做时,你会发现使用异常处理取消的原因 - 在命令式代码中执行此操作是一件痛苦的事情,所以你要么浪费了很多精力才能获得收益,要么你将转向更实用的编程方式(来吧,我们有cookies!*).
(*)免责声明:我们实际上没有cookies.但你可以自己做!