【发布时间】: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);
}
}
请注意,CancellationTokenSource 和 CancellationTokenRegistration 在当前类的 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