ASP.NET Core 7 Web API并发投票时如何避免数据更新冲突?
解决ASP.NET Core投票API的并发票数更新问题
问题根源分析
你的代码存在两个核心问题导致并发投票时票数异常:
- 自增操作逻辑错误:
currVote++是后置自增运算符,会先将currVote的原始值赋值给candidate.VoteCount,再对currVote自增,这意味着实际票数根本没有增加(单线程测试场景下可能未暴露,但本质是逻辑错误)。 - 线程安全问题:由于仓储通过
AddSingleton注册,多个并发请求会同时访问共享的_candidates集合和候选人对象,导致多个线程同时读取同一个VoteCount值,各自自增后覆盖结果,最终只保留一次自增效果。
修复方案
1. 修正自增逻辑
直接对候选人的VoteCount进行自增操作,避免临时变量带来的错误:
public void VoteCandidate(int id) { Candidate candidate = _candidates.FirstOrDefault(a => a.Id == id); if (candidate != null) { // 直接自增,无需临时变量 candidate.VoteCount++; } }
2. 添加线程同步保证并发安全
针对Singleton仓储的共享资源访问,有两种常用的线程安全处理方式:
方式一:使用lock语句实现排他访问
在仓储类中定义私有锁对象,确保同一时间只有一个线程能执行票数更新逻辑:
public class VoteRepository { private readonly List<Candidate> _candidates; // 定义锁对象 private readonly object _voteLock = new object(); public VoteRepository() { // 初始化候选人数据 _candidates = new List<Candidate> { new Candidate { Id = 1, CandidateName = "John Doe", VoteCount = 0 }, new Candidate { Id = 2, CandidateName = "Max Smith", VoteCount = 0 }, new Candidate { Id = 3, CandidateName = "Mark Spencer", VoteCount = 0 } }; } public void VoteCandidate(int id) { lock (_voteLock) { Candidate candidate = _candidates.FirstOrDefault(a => a.Id == id); if (candidate != null) { candidate.VoteCount++; } } } // GetCandidateById等其他方法... }
方式二:使用Interlocked进行原子操作
对于int类型的票数自增,可用Interlocked.Increment实现无锁的原子操作,性能更优:
public void VoteCandidate(int id) { Candidate candidate = _candidates.FirstOrDefault(a => a.Id == id); if (candidate != null) { // 原子性自增,确保多线程下不会丢失更新 Interlocked.Increment(ref candidate.VoteCount); } }
额外说明
- 后续引入数据库后,数据库的事务和行级锁(如SQL Server的
UPDATE ... WHERE语句)可自动处理并发问题,但当前内存仓储场景必须手动处理线程安全。 - Blazor WebAssembly端若存在频繁投票场景,建议添加防抖或节流逻辑,减少不必要的并发请求。
内容的提问来源于stack exchange,提问作者ArcDotNet
相关产品推荐
相关产品推荐

