fix(async): 修复异步扩展中的超时和重试逻辑问题

- 修复TimeoutAfter方法中的取消令牌处理逻辑,避免OperationCanceledException被意外捕获
- 修复RetryAsync方法中的参数验证,移除对可空值类型的空值检查
- 为RetryAsync方法添加throwOriginal参数以控制异常抛出行为
- 统一超时处理中的令牌取消方式,使用linkedCts.Cancel()替代timeoutCts.CancelAsync()
This commit is contained in:
GeWuYou 2026-02-25 17:03:00 +08:00 committed by gewuyou
parent 475f301d9f
commit 850fecdff4
2 changed files with 29 additions and 16 deletions

View File

@ -36,13 +36,22 @@ public static class AsyncExtensions
if (completedTask == delayTask)
{
// 优先检查外部取消令牌,若已取消则抛出 OperationCanceledException
cancellationToken.ThrowIfCancellationRequested();
throw new TimeoutException($"操作在 {timeout.TotalSeconds} 秒后超时");
}
await timeoutCts.CancelAsync();
return await task;
await linkedCts.CancelAsync();
try
{
await task;
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested &&
timeoutCts.Token.IsCancellationRequested)
{
// ignore
}
return task.Result;
}
/// <summary>
@ -69,13 +78,20 @@ public static class AsyncExtensions
if (completedTask == delayTask)
{
// 优先检查外部取消令牌,若已取消则抛出 OperationCanceledException
cancellationToken.ThrowIfCancellationRequested();
throw new TimeoutException($"操作在 {timeout.TotalSeconds} 秒后超时");
}
await timeoutCts.CancelAsync();
await task;
await linkedCts.CancelAsync();
try
{
await task;
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested &&
timeoutCts.Token.IsCancellationRequested)
{
// ignore
}
}
/// <summary>
@ -86,6 +102,7 @@ public static class AsyncExtensions
/// <param name="maxRetries">最大重试次数</param>
/// <param name="delay">重试间隔</param>
/// <param name="shouldRetry">判断是否应该重试的函数,默认对所有异常重试</param>
/// <param name="throwOriginal">当为 true 时直接抛出原始异常,否则包装为 AggregateException</param>
/// <returns>任务结果</returns>
/// <exception cref="ArgumentNullException">当 taskFactory 为 null 时抛出</exception>
/// <exception cref="ArgumentOutOfRangeException">当 maxRetries 小于 0 时抛出</exception>
@ -99,7 +116,8 @@ public static class AsyncExtensions
this Func<Task<T>> taskFactory,
int maxRetries,
TimeSpan delay,
Func<Exception, bool>? shouldRetry = null)
Func<Exception, bool>? shouldRetry = null,
bool throwOriginal = false)
{
ArgumentNullException.ThrowIfNull(taskFactory);
@ -123,6 +141,9 @@ public static class AsyncExtensions
}
else
{
if (throwOriginal)
throw;
throw new AggregateException($"操作在 {attempt} 次重试后仍然失败", ex);
}
}

View File

@ -23,10 +23,6 @@ public static class NumericExtensions
/// </example>
public static T Clamp<T>(this T value, T min, T max) where T : IComparable<T>
{
ArgumentNullException.ThrowIfNull(value);
ArgumentNullException.ThrowIfNull(min);
ArgumentNullException.ThrowIfNull(max);
if (min.CompareTo(max) > 0)
throw new ArgumentException($"最小值 ({min}) 不能大于最大值 ({max})");
@ -59,10 +55,6 @@ public static class NumericExtensions
/// </example>
public static bool Between<T>(this T value, T min, T max, bool inclusive = true) where T : IComparable<T>
{
ArgumentNullException.ThrowIfNull(value);
ArgumentNullException.ThrowIfNull(min);
ArgumentNullException.ThrowIfNull(max);
if (min.CompareTo(max) > 0)
throw new ArgumentException($"最小值 ({min}) 不能大于最大值 ({max})");
@ -109,4 +101,4 @@ public static class NumericExtensions
return (value - from) / (to - from);
}
}
}