WPF MVVM 从线程池更新 ObservableCollection

WPF MVVM updating ObservableCollection from Threadpool

我读到我从后台工作线程(使用 Task.Run())更新可观察集合的实现可能不是线程安全的。

在我的应用程序中,我有一个 ListView 绑定到一个可观察的集合,所选项目也双向绑定到我的视图模型。当应用程序空闲时,我让这个 ListView 每 60 秒更新一次它的值。此更新可以手动触发,因此我将其设计为异步函数,因为它从数据库请求值。当我的控件加载并处于空闲状态时,循环函数是运行 under Task.Run().

public ObservableCollection<WorkFlowTask> WorkFlowTasks { get; set; }
private WorkFlowTask _SelectedTask; 
        public WorkFlowTask SelectedTask
        {
            get
            {
                return _SelectedTask;
            }
            set
            {
                _SelectedTask = value;
                OnPropertyChanged("SelectedTask");
            }
        }

private void SynchTaskList()
        {
            TokenSourceTaskList = new CancellationTokenSource();
            CancelTokenTaskList = TokenSourceTaskList.Token;

            Task.Run(() => UpdateTaskList());
        }

UpdateTaskList 定义如下:

private async void UpdateTaskList ()
        {
            while (!CancelTokenTaskList.IsCancellationRequested)
            {
                List<WorkFlowTask> tasks = await Connector.GetWorkFlowTasksByEmpID(Employee.EmployeeID);
                if(SelectedTask != null)
                {
                    //Preserve currently selected item through collection update
                    SelectedTask = tasks.Where(x => x.TransWorkFlowDetailID == SelectedTask.TransWorkFlowDetailID).FirstOrDefault();
                }                
                
                var newWorkFlowTasks = new ObservableCollection<WorkFlowTask>(tasks.OrderBy(x => x.DueDate));
                
                if (!WorkFlowTasks.SequenceEqual(newWorkFlowTasks))
                {
                    WorkFlowTasks = newWorkFlowTasks;
                }
                OnPropertyChanged("WorkFlowTasks");
                await Task.Delay(60000);
            }            
        }

从功能上讲,这与我预期的一样,但我担心这可能不是线程安全的。这只是因为我用新对象替换集合而不是修改它才起作用吗?是否需要调用集合的更新?我是否也应该锁定 SelectedItem 属性 更改?

如有任何见解或建议,我们将不胜感激。

首先,您不需要调用对 SelectedTaskWorkflowTasks 的更改,因为您是通过数据绑定使用它们的,这本身就是异步的。正常访问collection就可以了。

由于您编写了可以手动调用 WorkflowTasks 的更新机制,因此将它们包装在 lock 语句中可能是个好主意。否则,当两个并行任务同时修改 SelectedTask 时,您可能 运行 会遇到问题。

我会在获取任务列表后立即锁定。这样多个任务可以同时获取列表(我猜这是一个 time-consuming 任务),但会按顺序将结果写入您的属性。

private async void UpdateTaskList()
    {
        while (!CancelTokenTaskList.IsCancellationRequested)
        {
            List<WorkFlowTask> tasks = await Connector.GetWorkFlowTasksByEmpID(Employee.EmployeeID);

            lock (this.taskListLock)
            {
                if (SelectedTask != null)
                {
                    //Preserve currently selected item through collection update
                    SelectedTask = tasks.Where(x => x.TransWorkFlowDetailID == SelectedTask.TransWorkFlowDetailID).FirstOrDefault();
                }

                var newWorkFlowTasks = new ObservableCollection<WorkFlowTask>(tasks.OrderBy(x => x.DueDate));

                if (!WorkFlowTasks.SequenceEqual(newWorkFlowTasks))
                {
                    WorkFlowTasks = newWorkFlowTasks;
                }
                OnPropertyChanged("WorkFlowTasks");
            }

            await Task.Delay(60000);
        }
    }