WPF MVVM下集合快速变更导致CPU占用过高、UI近乎冻结问题
根据你的需求,我整理了一套基于MVVM、MVVM Light的Windows进程监控实现方案,完美匹配任务管理器的核心功能(实时内存显示、进程启停监控),具体如下:
核心实现方案
1. 定义进程数据模型(Model层)
首先需要一个封装进程关键信息的模型类,借助MVVM Light的ObservableObject实现属性变更通知,确保内存占用变化能实时同步到UI:
using System.Diagnostics; using GalaSoft.MvvmLight; public class ProcessInfo : ObservableObject { private int _id; private string _processName; private long _privateWorkingSetMB; private Process _process; public int Id { get => _id; set => Set(ref _id, value); } public string ProcessName { get => _processName; set => Set(ref _processName, value); } // 私有工作集,转换为MB单位 public long PrivateWorkingSetMB { get => _privateWorkingSetMB; set => Set(ref _privateWorkingSetMB, value); } public Process Process { get => _process; set => Set(ref _process, value); } }
2. ViewModel层实现核心业务逻辑
ViewModel作为逻辑核心,负责处理进程加载、实时更新、启停监控三个核心任务,继承MVVM Light的ViewModelBase:
2.1 基础属性与命令初始化
using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using System.Collections.ObjectModel; using System.Diagnostics; using System.Timers; using System.Linq; public class MainViewModel : ViewModelBase { private bool _isMonitoring; private ObservableCollection<ProcessInfo> _monitoredProcesses; private Timer _memoryUpdateTimer; private Timer _processWatchTimer; // 目标进程名,这里以Chrome为例,可按需修改 private readonly string _targetProcessName = "chrome"; // 绑定复选框的监控开关 public bool IsMonitoring { get => _isMonitoring; set { Set(ref _isMonitoring, value); value ? StartMonitoring() : StopMonitoring(); } } // 绑定DataGrid的进程集合 public ObservableCollection<ProcessInfo> MonitoredProcesses { get => _monitoredProcesses; set => Set(ref _monitoredProcesses, value); } // 切换监控状态的命令 public ICommand ToggleMonitoringCommand { get; } public MainViewModel() { MonitoredProcesses = new ObservableCollection<ProcessInfo>(); ToggleMonitoringCommand = new RelayCommand(() => IsMonitoring = !IsMonitoring); // 1秒更新一次内存,和任务管理器频率一致 _memoryUpdateTimer = new Timer(1000); _memoryUpdateTimer.Elapsed += UpdateProcessMemoryUsage; // 2秒扫描一次进程启停,平衡性能与实时性 _processWatchTimer = new Timer(2000); _processWatchTimer.Elapsed += CheckProcessStartsAndExits; } }
2.2 启动监控与加载初始进程
private void StartMonitoring() { // 加载当前已运行的目标进程 LoadExistingTargetProcesses(); _memoryUpdateTimer.Start(); _processWatchTimer.Start(); } private void LoadExistingTargetProcesses() { foreach (var process in Process.GetProcessesByName(_targetProcessName)) { AddProcessToCollection(process); } } private void AddProcessToCollection(Process process) { try { // 捕获无权限访问的进程(比如系统核心进程) var procInfo = new ProcessInfo { Id = process.Id, ProcessName = process.ProcessName, PrivateWorkingSetMB = process.PrivateMemorySize64 / 1024 / 1024, Process = process }; MonitoredProcesses.Add(procInfo); } catch (System.ComponentModel.Win32Exception) { // 可在这里记录日志或忽略无权限进程 } }
2.3 实时更新内存占用
private void UpdateProcessMemoryUsage(object sender, ElapsedEventArgs e) { // 必须在UI线程更新UI元素,借助MVVM Light的DispatcherHelper DispatcherHelper.CheckBeginInvokeOnUI(() => { for (int i = MonitoredProcesses.Count - 1; i >= 0; i--) { var procInfo = MonitoredProcesses[i]; try { if (!procInfo.Process.HasExited) { procInfo.Process.Refresh(); procInfo.PrivateWorkingSetMB = procInfo.Process.PrivateMemorySize64 / 1024 / 1024; } else { MonitoredProcesses.Remove(procInfo); } } catch { // 进程无法访问时直接移除 MonitoredProcesses.Remove(procInfo); } } }); }
2.4 监控进程启停
private void CheckProcessStartsAndExits(object sender, ElapsedEventArgs e) { DispatcherHelper.CheckBeginInvokeOnUI(() => { var currentProcessIds = MonitoredProcesses.Select(p => p.Id).ToHashSet(); var runningTargetProcesses = Process.GetProcessesByName(_targetProcessName); // 新增刚启动的进程 foreach (var process in runningTargetProcesses) { if (!currentProcessIds.Contains(process.Id)) { AddProcessToCollection(process); } } // 移除已退出的进程(双重校验,确保无遗漏) for (int i = MonitoredProcesses.Count - 1; i >= 0; i--) { var procInfo = MonitoredProcesses[i]; if (procInfo.Process.HasExited || !runningTargetProcesses.Any(p => p.Id == procInfo.Id)) { MonitoredProcesses.Remove(procInfo); } } }); }
2.5 停止监控与资源释放
private void StopMonitoring() { _memoryUpdateTimer.Stop(); _processWatchTimer.Stop(); MonitoredProcesses.Clear(); } // 记得在ViewModel销毁时释放资源 public override void Cleanup() { _memoryUpdateTimer.Dispose(); _processWatchTimer.Dispose(); base.Cleanup(); }
3. View层(XAML)实现
DataGrid绑定进程集合,复选框绑定监控开关,实现UI与逻辑的解耦:
<Window x:Class="ProcessMonitor.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:ProcessMonitor.ViewModels" Title="进程监控工具" Height="450" Width="800"> <Window.DataContext> <vm:MainViewModel /> </Window.DataContext> <Grid Margin="10"> <StackPanel> <CheckBox Content="监控Chrome进程" IsChecked="{Binding IsMonitoring}" Margin="0 0 0 10" /> <DataGrid ItemsSource="{Binding MonitoredProcesses}" AutoGenerateColumns="False" CanUserAddRows="False"> <DataGrid.Columns> <DataGridTextColumn Header="进程名称" Binding="{Binding ProcessName}" Width="*" /> <DataGridTextColumn Header="私有工作集(MB)" Binding="{Binding PrivateWorkingSetMB}" Width="150" /> <DataGridTextColumn Header="进程ID" Binding="{Binding Id}" Width="100" /> </DataGrid.Columns> </DataGrid> </StackPanel> </Grid> </Window>
4. 关键注意事项
- UI线程初始化:记得在
App.xaml.cs的OnStartup方法中初始化DispatcherHelper:DispatcherHelper.Initialize(); - 性能优化:如果需要监控大量进程,建议用WMI事件监听(
Win32_ProcessStartTrace和Win32_ProcessStopTrace)替代定时扫描,减少系统开销 - 权限处理:部分系统进程需要管理员权限才能访问,可在项目属性中设置“需要管理员权限”
内容的提问来源于stack exchange,提问作者BertAR
相关产品推荐
相关产品推荐

