You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor WASM中Parallel与async配合时List.Add结果异常排查

问题根源
  1. 核心原因是List<T>不是线程安全类型:你的代码通过Parallel.ForEachAsync并发执行Test4方法,多个线程会同时调用emptyTestlist.Add(i)修改集合,并发写入会破坏List内部的下标计数、扩容逻辑,导致部分数据被覆盖丢失,所以Count经常小于4。
  2. 添加Console.WriteLine后恢复正常是巧合:Console.WriteLine内部自带全局同步锁,执行时会强制线程排队,同时引入了微小的时间差,刚好错开了并发写入的冲突窗口,但这不是可靠的修复方案,只要CPU负载、运行环境变化,问题依旧会复现。

另外你现有代码还有一个隐藏问题:Parallel.Invoke接收异步lambda时会生成async void方法,执行过程中如果抛出未捕获异常会直接导致进程崩溃,不建议这么用。

解决方案

你可以根据自己的场景任选以下一种方案:

  • 方案1:给集合写入操作加同步锁
    定义全局锁对象,在修改emptyTestlist时加锁,确保同一时间只有一个线程修改集合:

    // 定义锁对象
    private readonly object _listLock = new object();
    
    async Task Test4(int i)
    {
        await Task.Delay(300);
        // 加锁保证写入安全
        lock(_listLock)
        {
            emptyTestlist.Add(i);
        }
        Console.WriteLine($"from TEST4: {i}");
    }
    
  • 方案2:使用线程安全集合
    直接将emptyTestlist的类型替换为System.Collections.Concurrent命名空间下的线程安全集合,例如ConcurrentBag<int>,不需要手动加锁就可以支持并发写入:

    // 替换原来的List<int> emptyTestlist定义
    ConcurrentBag<int> emptyTestlist = new ConcurrentBag<int>();
    
  • 方案3(最推荐):避免共享集合写入,直接收集并发任务返回值
    你的场景完全不需要共享可变集合,只需要让每个Test4返回处理结果,等待所有任务完成后统一收集结果即可,从根源上消除并发冲突:

    async ValueTask Test2()
    {
        cts = new CancellationTokenSource();
        // 并发执行所有Test4,统一收集返回结果
        var results = await Task.WhenAll(testList.Select(test => Test4(test)));
        emptyTestlist = results.ToList();
        
        foreach (var test in emptyTestlist)
        {
            await Test3(test);
        }
    }
    
    // 修改Test4返回处理后的结果
    async Task<int> Test4(int i)
    {
        await Task.Delay(300);
        Console.WriteLine($"from TEST4: {i}");
        return i;
    }
    

另外针对你的Blazor WASM场景,建议把定时循环逻辑改成托管服务或者在组件生命周期中用CancellationToken控制启停,避免组件销毁后后台任务继续运行导致内存泄漏。


内容的提问来源于stack exchange,提问作者otter otter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 04:18:01