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

为何SemaphoreSlim无法阻止C# Avalonia应用的竞态条件?

问题分析与修复

错误原因

你的代码存在两个关键问题,导致加载未完成时保存操作仍会执行:

  1. 检查与执行的非原子性竞态:SaveUserData中通过mySemaphore.CurrentCount == 0判断加载状态,但这个检查和后续的保存操作不是原子操作。哪怕检查时信号量可用(加载完成),在执行保存的瞬间,加载任务可能刚好获取了信号量,最终导致加载未完成但保存执行。
  2. 异步任务启动延迟: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 02:31:19