我有这样的情况:
private Task LongRunningTask = /* Something */;
private void DoSomethingMore(Task previousTask) { }
public Task IndependentlyCancelableSuccessorTask(CancellationToken cancellationToken)
{
return LongRunningTask.ContinueWith(DoSomethingMore, cancellationToken);
}特别是,我在这里感兴趣的行为在MSDN's page about Continuation Tasks中详细描述如下:
在这些场景中,继续进入
Canceled状态:
System.Threading.CancellationToken作为参数,并且令牌的IsCancellationRequested属性在继续运行之前是true。在这种情况下,延续不会启动,它将转换到Canceled状态。上面的代码起作用。但是,我正在将尽可能多的延续转换为使用await关键字。
是否有等效的使用await允许在等待的任务完成之前取消延续?
发布于 2014-01-09 11:37:38
以下几点应该可以做到,尽管看起来有点尴尬:
private Task LongRunningTask = /* Something */;
private void DoSomethingMore() { }
public async Task IndependentlyCancelableSuccessorTask(
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(() => tcs.TrySetCanceled()))
await Task.WhenAny(LongRunningTask, tcs.Task);
cancellationToken.ThrowIfCancellationRequested();
DoSomethingMore();
}按照svick的建议更新,这里它被塑造为一个助手,基于Stephen的Implementing Then with Await模式:
public static class TaskExt
{
/// <summary>
/// Use: await LongRunningTask.Then(DoSomethingMore, cancellationToken)
/// </summary>
public static async Task Then(
this Task antecedent, Action continuation, CancellationToken token)
{
await antecedent.When(token);
continuation();
}
/// <summary>
/// Use: await LongRunningTask.When(cancellationToken)
/// </summary>
public static async Task When(
this Task antecedent, CancellationToken token)
{
token.ThrowIfCancellationRequested();
var tcs = new TaskCompletionSource<Empty>();
using (token.Register(() => tcs.TrySetCanceled()))
await Task.WhenAny(antecedent, tcs.Task);
token.ThrowIfCancellationRequested();
}
struct Empty { };
}也许,第一个ThrowIfCancellationRequested()是多余的,但我还没有彻底考虑过所有的边缘情况。
发布于 2014-01-09 14:54:43
虽然这个答案在概念上与诺塞雷肖的相同,但我对实施的一些细节并不满意,因此,我现正公布我建议的“帮助者实施计划”,以便其他人可以就这个问题发表意见。
public static async Task<TResult> WhenNotCanceled<TResult>(this Task<TResult> mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled) {
return await mainTask.ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
Task<TResult> completedTask;
var cancellationTaskSource = new TaskCompletionSource<TResult>();
using (cancellationToken.Register(() => cancellationTaskSource.TrySetCanceled(), useSynchronizationContext: false)
completedTask = await Task.WhenAny(mainTask, cancellationTaskSource.Task).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
return await completedTask.ConfigureAwait(false);
}
public static async Task WhenNotCanceled(this Task mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled) {
await mainTask.ConfigureAwait(false);
return;
}
cancellationToken.ThrowIfCancellationRequested();
Task completedTask;
var cancellationTaskSource = new TaskCompletionSource<object>();
using (cancellationToken.Register(() => cancellationTaskSource.TrySetCanceled(), useSynchronizationContext: false)
completedTask = await Task.WhenAny(mainTask, cancellationTaskSource.Task).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
await completedTask.ConfigureAwait(false);
}异步模式而不取消:
public async Task IndependentlyCancelableSuccessorTask()
{
await LongRunningTask;
DoSomethingMore();
}带有cancel和WhenNotCanceled的异步模式
public async Task IndependentlyCancelableSuccessorTask(CancellationToken cancellationToken)
{
await LongRunningTask.WhenNotCanceled(cancellationToken);
DoSomethingMore();
}发布于 2014-10-09 15:38:59
我的回答与“让·霍米纳”的回答略有不同,并采纳了“诺塞雷肖”的方法:
public static class TaskExtensionMethods
{
public static Task<TResult> OrWhenCancelled<TResult>(this Task<TResult> mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
return mainTask;
return OrWhenCancelled_(mainTask, cancellationToken);
}
private static async Task<TResult> OrWhenCancelled_<TResult>(this Task<TResult> mainTask, CancellationToken cancellationToken)
{
Task cancellationTask = Task.Delay(Timeout.Infinite, cancellationToken);
await Task.WhenAny(mainTask, cancellationTask).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
return await mainTask;
}
public static Task OrWhenCancelled(this Task mainTask, CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
return mainTask;
return OrWhenCancelled_(mainTask, cancellationToken);
}
private static async Task OrWhenCancelled_(this Task mainTask, CancellationToken cancellationToken)
{
Task cancellationTask = Task.Delay(Timeout.Infinite, cancellationToken);
await Task.WhenAny(mainTask, cancellationTask).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
await mainTask;
}
}讨论:
ContinueWith指定TaskScheduler的情况。具体来说,考虑一下为在UI场景中使用而创建的TaskScheduler。在这种情况下,使用原始的ContinueWith方法,您可以保证在运行委托之前检查取消令牌,但在进入主线程之后检查取消令牌(请参阅this answer)。也就是说,旧的方法在考虑任务的结果之前,会在主线程上检查取消令牌“最后一次”(即超越主任务完成还是错误)。这意味着,除了使用这些扩展方法之外,新代码还必须将其await包装在try/ CancellationToken中,以完成对CancellationToken :( )的最后检查。见this question。await的一大优势:代码不会以委托结束,它只是在await之后,并且读起来像正常的顺序代码。备注:
https://stackoverflow.com/questions/21017665
复制相似问题