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 属性 更改?
如有任何见解或建议,我们将不胜感激。
首先,您不需要调用对 SelectedTask
和 WorkflowTasks
的更改,因为您是通过数据绑定使用它们的,这本身就是异步的。正常访问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);
}
}
我读到我从后台工作线程(使用 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 属性 更改?
如有任何见解或建议,我们将不胜感激。
首先,您不需要调用对 SelectedTask
和 WorkflowTasks
的更改,因为您是通过数据绑定使用它们的,这本身就是异步的。正常访问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);
}
}