同步内存数据源下使用Async API是否合理?两种实现对比解析
同步数据源下使用异步API是否更优?
我在内存中存储了JSON字符串列表,读取代码如下:
List<string> data = new List<string>(); using (StreamReader sr = new StreamReader("file.txt")) { while (sr.Peek() >= 0) { data.Add(sr.ReadLine()); } }
我尝试用System.Linq.Async对这些数据过滤,实现代码如下:
var filteredData = data.WhereAwait(async x => await filters.ToAsyncEnumerable().AllAwaitAsync(async filter => (await JsonSerializer.DeserializeAsync<RootDto>(new MemoryStream(Encoding.UTF8.GetBytes(x)))).Child.Id == filter));
我知道这个示例比较粗糙,只是用来探讨同步数据源场景下使用异步API是否有意义。这里的List<string> data本质是同步数据源,我明白异步数据源用异步API更合理。现在想知道:上面的异步实现,是否比下面这种同步阻塞的实现更优?
同步阻塞实现代码:
var filters = new[] {"U45TC0Y7KLLD", "ELXE5X1IXHZV", "KHI3NXKEN1PE", "A7R5LDD4O0NK", "1TIICSXMH7FJ"}; var filteredData = data.Where(x => filters.All(filter => JsonSerializer.DeserializeAsync<RootDto>(new MemoryStream(Encoding.UTF8.GetBytes(x))).GetAwaiter().GetResult().Child.Id == filter));
详细分析对比
1. 你的异步实现不仅不优,还额外增加开销
- 无意义的异步包装:
data是内存中的同步集合,WhereAwait和ToAsyncEnumerable只是把同步操作强行套上异步壳,不会带来任何并发或IO调度的优势,反而会因为异步状态机的创建、上下文切换产生额外性能损耗。 - 重复反序列化的浪费:每个JSON字符串
x会被反序列化多次——遍历每个filter时都会重新执行JsonSerializer.DeserializeAsync,这会大幅消耗CPU和内存资源。 - 并行度失控风险:如果没有限制并行数量,异步API可能同时发起大量反序列化操作,导致线程池资源被占满,反而拖慢整体处理速度。
2. 同步阻塞实现的问题
- 线程阻塞的隐患:用
GetAwaiter().GetResult()直接阻塞线程等待异步反序列化完成,会造成线程浪费;如果是在UI线程或ASP.NET请求线程中,还可能触发死锁(当同步上下文被占用时)。 - 同样存在重复反序列化:和异步实现一样,每个
x会被每个filter触发一次反序列化,这是两种实现共同的低效核心。
3. 真正更优的实现思路
不管选异步还是同步,首先要解决重复反序列化的问题,再结合场景选择合适模式:
// 先一次性把所有JSON字符串反序列化为RootDto,避免重复操作 var deserializedData = data.Select(x => JsonSerializer.Deserialize<RootDto>(x)).ToList(); // 再执行过滤逻辑 var filteredData = deserializedData.Where(dto => filters.All(filter => dto.Child.Id == filter));
这里用同步的JsonSerializer.Deserialize即可——因为数据在内存中,反序列化是CPU绑定操作,异步版本不会带来任何好处,反而会增加不必要的开销。这种实现的性能会远高于你给出的两种方案。
4. 结论
你的异步实现并不比同步阻塞实现更优,两者都存在重复反序列化的致命低效问题,而异步实现还多了异步包装的额外损耗。
异步API的优势在于IO绑定操作(比如文件读写、网络请求),能释放线程去处理其他任务;但对于内存中的CPU绑定操作(比如JSON反序列化),异步不仅不会提升性能,反而会拖慢速度。另外,同步阻塞实现中用GetResult()的做法在很多业务场景下是危险的,容易引发死锁或线程池饥饿问题。
内容的提问来源于stack exchange,提问作者kek101
相关产品推荐
相关产品推荐

