【问题标题】:How to avoid SetResult after task cancellation when using CancelAfter使用CancelAfter时如何避免任务取消后的SetResult
【发布时间】:2021-10-07 23:52:26
【问题描述】:

我将 EAP 转换为 TAP。但是,处理的事件可能根本不会被触发,这就是我添加超时的原因。查看示例代码

class Request<T> {
    TaskCompletionSource<T> tcs;
    CancellationTokenSource cts;
    public int Timeout { get; }
    public bool IsCanceled => tcs.Task.IsCanceled;

    public Request(int timeout = System.Threading.Timeout.Infinite) => Timeout = timeout;

    public Task<T> StartRequestAsync() {
        cts.Token.Register(() => tcs.SetCanceled());
        cts.CancelAfter(Timeout);
        return tcs.Task;
    }

    public void OnEvent(T result) {
        tcs.SetResult(result);
    }
}

请注意,CancellationTokenSourceCancellationTokenRegistration 在当前类的 IDisposable 的实现中都已正确处理。 StartRequestAsync 方法也被线程安全地阻止运行多次。为了简洁起见,代码被简化了。

现在,我在外部设置任务的结果。

var req = new Request<int>();
if(!req.IsCanceled) 
{
    req.SetResult(5);
}

但我认为存在竞争条件。我假设取消令牌CancelAfter 用作中断。如果令牌在执行进入if 块后被取消,我们可以得到InvalidOperationException: An attempt was made to transition a task to a final state when it had already completed.

文档指出 TaskCompletionSource 是线程安全的,那么这是否意味着 TrySetResult 是我在这里尝试实现的原子版本?那是我应该改用的吗?

有没有其他方法可以解决这个问题?

【问题讨论】:

  • 是的,TrySetResult 是原子的和线程安全的,并且会做你想做的事。
  • @canton7 谢谢
  • 也就是说,出于同样的原因,您可能也想要 TrySetCanceled

标签: c# task-parallel-library


【解决方案1】:

看看SetResultSetCanceled 方法是如何实现的可能会有所帮助:

public void SetResult(TResult result)
{
    if (!TrySetResult(result))
        throw new InvalidOperationException(
            Environment.GetResourceString("TaskT_TransitionToFinal_AlreadyCompleted"));
}

public void SetCanceled()
{
    if(!TrySetCanceled())
        throw new InvalidOperationException(
            Environment.GetResourceString("TaskT_TransitionToFinal_AlreadyCompleted"));
}

如您所见,这两种方法都委托给它们的 Try 对应方。您需要它们来检测代码中的错误,以防您的场景是确定性的。如果不是,并且场景中固有竞争条件,请使用 Try 版本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-21
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多