WPF ObservableCollection线程安全

时间:2011-08-15 15:46:39

标签: wpf multithreading mvvm thread-safety

我有一个MVVM设置。

我的模型定期调用某个服务,然后在ViewModel上调用一个动作,然后更新一些暴露给View的变量。

变量是ReadOnlyObservableCollection<T>,它有一个ObservableCollection<T>侦听。

问题是模型从另一个线程调用回调,因此它不允许我在另一个线程上清除ObservableCollection<T>

所以我想:使用调度程序,如果我们不在正确的线程上,请调用它:

    private void OnNewItems(IEnumerable<Slot> newItems)
    {
        if(!Dispatcher.CurrentDispatcher.CheckAccess())
        {
            Dispatcher.CurrentDispatcher.Invoke(new Action(() => this.OnNewItems(newItems)));
            return;
        }

        this._internalQueue.Clear();
        foreach (Slot newItem in newItems)
        {
            this._internalQueue.Add(newItem);
        }
    }

我认为代码非常简单。

问题在于,即使我在正确的线程上执行它(我认为)它仍然会在.Clear();上引发异常

为什么会这样?如何在不创建自定义ObservableCollection<T>的情况下解决此问题?

2 个答案:

答案 0 :(得分:2)

我通常会在公共视图模型库中初始化我的视图模型使用的调度程序,以帮助确保它是UI线程调度程序,正如Will提到的那样。

#region ViewModelBase()
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary>
protected ViewModelBase()
{
    _dispatcher = Dispatcher.CurrentDispatcher;
}
#endregion

#region Dispatcher
/// <summary>
/// Gets the dispatcher used by this view model to execute actions on the thread it is associated with.
/// </summary>
/// <value>
/// The <see cref="System.Windows.Threading.Dispatcher"/> used by this view model to 
/// execute actions on the thread it is associated with. 
/// The default value is the <see cref="System.Windows.Threading.Dispatcher.CurrentDispatcher"/>.
/// </value>
protected Dispatcher Dispatcher
{
    get
    {
        return _dispatcher;
    }
}
private readonly Dispatcher _dispatcher;
#endregion

#region Execute(Action action)
/// <summary>
/// Executes the specified <paramref name="action"/> synchronously on the thread 
/// the <see cref="ViewModelBase"/> is associated with.
/// </summary>
/// <param name="action">The <see cref="Action"/> to execute.</param>
protected void Execute(Action action)
{
    if (this.Dispatcher.CheckAccess())
    {
        action.Invoke();
    }
    else
    {
        this.Dispatcher.Invoke(DispatcherPriority.DataBind, action);
    }
}
#endregion

然后,您可以在视图模型调度程序上调用操作,如下所示:

this.Execute(
    () =>
    {
        this.OnNewItems(newItems);
    }
);

答案 1 :(得分:0)

在Codeproject- Multi-Threaded ObservableCollection and NotifyCollectionChanged Wrapper

上找到了解决此问题的简洁方法