错误的使用异步方法
使用List.ForEach(Async () => {await DoAsync()})会有哪些问题
- 异步方法不会被正确等待
- List.ForEach 的设计是同步的,不支持异步操作。
- 当你在 List.ForEach 中使用 async 方法时,ForEach 不会等待每个异步操作完成,而是直接返回,导致异步操作在后台运行。
- 这种行为可能会导致未完成的任务或逻辑错误(依赖注入的 scope 被释放)。
- 异步异常可能无法正确捕获
- 如果 DoAsync(item) 抛出了异常,异常可能不会被捕获,因为异步任务在 List.ForEach 返回后继续执行。
- 如果你没有显式处理任务异常,可能会导致未观察到的任务异常(UnobservedTaskException)。
- 并发问题
- List.ForEach 的设计是逐项执行,而不是并发执行。如果你希望异步任务能够并发执行,那么 List.ForEach 不适合。
- 即使你将异步任务写入 List.ForEach,任务之间的执行顺序仍然是同步的。
解决方案
- 使用 Parallel.ForEachAsync(.NET 6+)
csharp
List<int> items = new List<int> { 1, 2, 3, 4, 5 };
await Parallel.ForEachAsync(items, async (item, cancellationToken) =>
{
await DoAsync(item);
});- 扩展一个新的方法
csharp
public static async Task ForEachAsync<T>(this IEnumerable<T>? source, Func<T, Task> action)
{
if (source == null) return;
foreach (var value in source)
{
await action(value);
}
}- 使用task.WhenAll
csharp
var tasks = new List<Task>();
List.ForEach(Async () => {tasks.Add(DoAsync())})
await Task.WhenAll(tasks);controller中使用 _ = Task.Run(() => DoSomethingAsync())会有哪些风险
- 异步方法不会被正确等待
- Task.Run 会立即返回一个 Task 对象,而不会等待 DoSomethingAsync 完成。
- 如果 DoSomethingAsync 中有依赖于当前请求上下文的操作(如数据库操作、HttpContext 等),这些操作可能会在请求结束后执行,导致上下文丢失。
- 异步异常可能无法正确捕获
- 如果 DoSomethingAsync 抛出异常,异常可能不会被捕获,因为 Task.Run 返回的 Task 不会在当前请求上下文中处理。
- 如果没有显式处理任务异常,可能会导致未观察到的任务异常(UnobservedTaskException)。
- 并发问题
- Task.Run 会在后台线程池线程上执行 DoSomethingAsync,这可能导致并发执行。
- 如果 DoSomethingAsync 中有共享资源访问,可能会导致并发问题,如数据竞争或死锁。
解决方案
- 在 Task中使用
csharp
public string MergeRequest([FromBody] GitLabMergeWebhookRequest hook)
{
_ = Task.Factory.StartNew(async () =>
{
try
{
using (var scope = _scopeFactory.CreateScope())
{
var webhookService = scope.ServiceProvider.GetRequiredService<IWebhookService>();
var logger = scope.ServiceProvider.GetRequiredService<SkyNetLogger<WebhookController>>();
logger.LogInfo($"接收到数据: {JsonConvert.SerializeObject(hook)}", "webhook");
await webhookService.HandleAsync(hook);
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"接收结果异常: {JsonConvert.SerializeObject(hook)}", "webhook");
}
}, TaskCreationOptions.LongRunning);
return "";
}- 注入的服务使用Singleton(全局实例唯一)