首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >等价于ContinueWith(委托,CancellationToken)并等待继续

等价于ContinueWith(委托,CancellationToken)并等待继续
EN

Stack Overflow用户
提问于 2014-01-09 10:30:27
回答 4查看 1.7K关注 0票数 12

我有这样的情况:

代码语言:javascript
复制
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允许在等待的任务完成之前取消延续?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-01-09 11:37:38

以下几点应该可以做到,尽管看起来有点尴尬:

代码语言:javascript
复制
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模式:

代码语言:javascript
复制
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()是多余的,但我还没有彻底考虑过所有的边缘情况。

票数 6
EN

Stack Overflow用户

发布于 2014-01-09 14:54:43

虽然这个答案在概念上与诺塞雷肖的相同,但我对实施的一些细节并不满意,因此,我现正公布我建议的“帮助者实施计划”,以便其他人可以就这个问题发表意见。

代码语言:javascript
复制
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);
}

异步模式而不取消:

代码语言:javascript
复制
public async Task IndependentlyCancelableSuccessorTask()
{
    await LongRunningTask;
    DoSomethingMore();
}

带有cancel和WhenNotCanceled的异步模式

代码语言:javascript
复制
public async Task IndependentlyCancelableSuccessorTask(CancellationToken cancellationToken)
{
    await LongRunningTask.WhenNotCanceled(cancellationToken);
    DoSomethingMore();
}
票数 3
EN

Stack Overflow用户

发布于 2014-10-09 15:38:59

我的回答与“让·霍米纳”的回答略有不同,并采纳了“诺塞雷肖”的方法:

代码语言:javascript
复制
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
  • @ has的解决方案可以解决上述问题(如果需要的话),但它的缺点是需要将该问题继续放在委托中。在我看来,这违背了将代码转换为使用await的一大优势:代码不会以委托结束,它只是在await之后,并且读起来像正常的顺序代码。

备注:

  • 我希望我能够指定空的lambda从未运行(即,而不是只在取消时运行),但是.ContinueWith方法不允许这样做。所以,我(大多是任意选择OnlyOnCancelled)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21017665

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档