为何SemaphoreSlim无法阻止C# Avalonia应用的竞态条件?
问题分析与修复
错误原因
你的代码存在两个关键问题,导致加载未完成时保存操作仍会执行:
- 检查与执行的非原子性竞态:
SaveUserData中通过mySemaphore.CurrentCount == 0判断加载状态,但这个检查和后续的保存操作不是原子操作。哪怕检查时信号量可用(加载完成),在执行保存的瞬间,加载任务可能刚好获取了信号量,最终导致加载未完成但保存执行。 - 异步任务启动延迟:
LoadUserData通过Task.Run启动异步任务,该任务并非立即执行。如果应用在Task.Run的任务还没来得及调用mySemaphore.WaitAsync()就触发Exit事件,此时CurrentCount仍为1,SaveUserData会错误执行保存,而实际上加载还未开始。
另外,mySemaphore被定义为static,如果MainWindowViewModel存在多个实例,会导致不同实例间的信号量互相干扰,建议改为实例成员。
修复方案
修改SaveUserData方法,通过SemaphoreSlim.Wait(0)尝试立即获取信号量——这是原子性操作,能准确判断加载是否完成:
public sealed class MainWindowViewModel : ViewModelBase { // 改为实例成员,避免多实例干扰 private readonly SemaphoreSlim _mySemaphore = new(1, 1); public MainWindowViewModel() => LoadUserData(); private void LoadUserData() => Task.Run(async () => { await _mySemaphore.WaitAsync(); try { var items = await Task.WhenAll(UserDataManager.LoadUserItemsAsync()); Dispatcher.UIThread.Post(() => { foreach (var item in items) AddItem(item); }); } finally { _mySemaphore.Release(); } }); public void SaveUserData() { // 尝试立即获取信号量,失败则说明加载未完成 if (!_mySemaphore.Wait(0)) return; try { UserDataManager.SaveUserData(this.items); } finally { _mySemaphore.Release(); } } }
修复逻辑说明
_mySemaphore.Wait(0)会尝试立即获取信号量:返回true表示获取成功(加载已完成),此时可安全执行保存;返回false表示信号量被持有(加载中),直接跳过保存。- 将信号量改为实例成员,确保每个
MainWindowViewModel实例拥有独立的信号量,避免多实例场景下的干扰。 - 保存操作获取信号量后必须在
finally中释放,防止信号量泄漏。
内容的提问来源于stack exchange,提问作者Alexandre
相关产品推荐
相关产品推荐

