我想知道什么,我需要做,使模型线程安全的MVVM。 说我有以下类,它被实例化为单:
public class RunningTotal: INotifyPropertyChange
{
private int _total;
public int Total
{
get { return _total; }
set
{
_total = value;
PropertyChanged("Total");
}
}
...etc...
}
我的视图模型通过属性暴露它:
public RunningTotal RunningTotal { get; }
而我的观点上绑定了一个文本块,即{Binding Path=RunningTotal.Total}
我的应用程序有一个后台线程定期更新的总价值。 假设没有其他更新总,什么(如果有的话)应该怎么做才能让这一切线程安全的?
现在,如果我想要做类似的事情,但使用类型的属性Dictionary<>
或ObservableCollection<>
其成员(添加,删除,清除,索引)是线程安全的? 我应该使用ConcurrentDictionary呢?
Answer 1:
我的应用程序有一个后台线程定期更新的总价值。 假设没有其他更新总,什么(如果有的话)应该怎么做才能让这一切线程安全的?
对于标量的属性,你不需要做什么特别的事情; 在PropertyChanged
事件被自动封送到UI线程。
现在,如果我想要做类似的事情,但使用型词典<>的属性,或者的ObservableCollection <>? 其成员(添加,删除,清除,索引)是线程安全的? 我应该使用ConcurrentDictionary呢?
不,这不是线程安全的。 如果您更改的内容ObservableCollection<T>
从后台线程,它会打破。 你需要做的是在UI线程上。 一个简单的方法来做到这一点是使用提高了UI线程上的事件,如描述的集合在这里 。
作为Dictionary<TKey, TValue>
,当它的含量的变化,所以UI不是无论如何通知它不提高的通知。
Answer 2:
说有两个线程更新Total
,你要记录所有的变化_total
中PropertyChanged
方法。 现在存在一种竞争条件PropertyChanged
可错过的值。 这发生在调用中间当一个线程块set_Total
。 它更新_total
但尚未调用的PropertyChanged。 在此期间,另一个线程更新_total
到另一个值:
thread1: _total = 4;
thread2: _total = 5;
thread2: PropertyChanged("Total");
thread1: PropertyChanged("Total");
现在PropertyChanged
不会被调用为4的值。
可以通过将值到解决这个PropertyChanged
方法,或通过使用在设定器的锁。
既然你说你有一个线程用于更新此属性,不存在可能性的竞争条件。 这仅仅是当多个线程(或进程)更新在同一时间同一件事的情况。
Answer 3:
该模型应该写在一个线程安全的方式就像任何代码必须是; 它是由你来决定你是否使用锁,并发的容器或其他任何东西去做。 该模型只是库代码,其中(几乎)应该不知道,它的功能是要由MVVM应用程序使用。
虚拟机,但是,在UI线程工作。 这意味着,他们通常不能依靠从模型的事件在UI线程来了,让他们有当元帅的呼叫或将其存储在任务队列,如果订阅事件是在UI线程来不。
因此,虚拟机是应该以特定的方式关心线程安全的地方,超过了模型需要。
视图代码,反过来,通常生活在所有的线程问题快乐的无知:它得到的所有消息/电话/事件/无论在专用UI线程,并使其在UI线程自己的呼叫也是如此。
特别是对于你的情况,你的代码是不是模型,但虚拟机,对不对? 在这种情况下,你不得不解雇你的事件在UI线程,否则视图会不高兴。
Answer 4:
这个问题提供的ObservableCollection的线程编组版本
你如何正确地更新从后台线程数据绑定的DataGridView
但是,你仍然需要担心线程之间的竞争,这需要你的时候都更新到锁定资源,或使用类似Interlocked.Increment。
如果一个线程正在更新,而另一个在读时,可能出现读出通过一更新完成了一半的方式(例如,一个Int64被修改,所述第一半(32位)已在一个线程更新方法可行,和下半年已经更新之前,该值从第二个线程读取。一个完全错误的值被读取)
这可能是也可能不是取决于你的应用程序会做,结果有问题。 如果错误的值将在GUI上闪现1秒,那么它可能不是一个大不了的,锁的性能损失可以忽略不计。 如果你的程序将基于该值来采取行动,那么你可能需要将其锁定。
Answer 5:
一个简单的答案是,你需要通过UI线程的调度安排在UI线程的属性更新。 这将使更新操作,不会导致应用程序崩溃的队列。
private void handler(object sender, EventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate { updates(); });
}
private void updates() { /* real updates go here */ }
Answer 6:
有些事情更复杂,它其实就是这么简单......当您的视图实例化视图模型您刚刚通过调度员倒在构造函数。
ServerOperationViewmodel ViewModel;
public pgeServerOperations()
{
InitializeComponent();
ViewModel = new ServerOperationViewmodel(Dispatcher);
}
然后在您的视图模型:
Dispatcher UIDispatcher;
public ServerOperationViewmodel(Dispatcher uiDisp)
{
UIDispatcher = uiDisp;
}
并使用它像一个正常的UI调度。
UIDispatcher.Invoke(() =>
{
.......
});
我承认,我还是相当新的MVVM,但我不不认为这打破了MVVM座右铭。
文章来源: C# thread safety (in particular MVVM/WPF)