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

.NET是否内置支持带计数的ViewModel IsBusy状态处理?

问题描述

我的视图模型(ViewModel)使用IsBusy属性指示后台处理是否进行,以此驱动UI指示器。但忙碌状态并非互斥,有时多个后台操作可同时进行,因此我用计数而非布尔值来维护IsBusy状态,定义了如下IBusy接口:

public interface IBusy
{
    void IncrementBusyCount();
    bool DecrementBusyCount();
    bool IsBusy { get; }
}

我在视图模型上实现该接口,使IsBusy在首个后台操作启动时变为true,最后一个操作结束时变为false:

public class MyBusyVm : ViewModelBase, IBusy { /* ... */}

我还编写了实现IDisposable的BusyLock包装类,自动调用IncrementBusyCount和DecrementBusyCount,无需每次使用try/finally:

void DoWorkOnBackgroundThread(MyBusyVm vm)
{
    using myLock = new BusyLock(vm);

    // ...执行耗时后台工作 
    // ...计数在此自动递减
}

这是很常见的设计模式,我认为.NET中应该已有更完善的内置实现,是否存在?(多年前我曾自行实现Lazy和ISupportInitialize,不想重蹈覆辙)(补充:我知道可以用Semaphore,但希望避免内核对象)

解答

.NET框架及.NET Core/.NET 5+中没有完全匹配该需求的内置开箱实现,但你可以借助框架提供的基础类型简化实现,无需从头构建核心逻辑:

  • 原子计数用Interlocked类替代手动实现:多线程场景下的计数增减需要保证原子性,Interlocked.Increment和Interlocked.Decrement是用户态操作,不会用到内核对象,完美契合你的性能要求,同时能确保线程安全。
  • BusyLock这类IDisposable包装器虽无内置,但可基于Interlocked快速实现:结合Interlocked的原子操作,你可以写出轻量且可靠的版本,示例如下:
// 基于Interlocked实现的IBusy(适配MVVM的通知机制)
public class BusyManager : IBusy, INotifyPropertyChanged
{
    private int _busyCount;

    public bool IsBusy => _busyCount > 0;

    public void IncrementBusyCount()
    {
        Interlocked.Increment(ref _busyCount);
        OnPropertyChanged(nameof(IsBusy));
    }

    public bool DecrementBusyCount()
    {
        var newCount = Interlocked.Decrement(ref _busyCount);
        OnPropertyChanged(nameof(IsBusy));
        return newCount >= 0;
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// 轻量BusyLock实现
public readonly struct BusyLock : IDisposable
{
    private readonly IBusy _busyManager;

    public BusyLock(IBusy busyManager)
    {
        _busyManager = busyManager;
        _busyManager.IncrementBusyCount();
    }

    public void Dispose()
    {
        _busyManager?.DecrementBusyCount();
    }
}

这种实现既满足了多线程下的计数需求,又避免了内核对象的开销,同时复用了.NET内置的原子操作逻辑,不用重复造轮子。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 16:37:13