使用 CancelAfter 时如何在任务取消后避免 SetResult
How to avoid SetResult after task cancellation when using CancelAfter
我将 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);
}
}
请注意,在当前 class 的 IDisposable
的实施中,CancellationTokenSource
和 CancellationTokenRegistration
均已正确处理。 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
是我在这里尝试实现的原子版本?那是我应该改用的吗?
还有其他方法可以解决吗?
了解 SetResult
and SetCanceled
方法的实现方式可能会有所帮助:
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
版本。
我将 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);
}
}
请注意,在当前 class 的 IDisposable
的实施中,CancellationTokenSource
和 CancellationTokenRegistration
均已正确处理。 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
是我在这里尝试实现的原子版本?那是我应该改用的吗?
还有其他方法可以解决吗?
了解 SetResult
and SetCanceled
方法的实现方式可能会有所帮助:
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
版本。