C#线程安全(特别是MVVM / WPF)(C# thread safety (in particu

2019-09-18 02:19发布

我想知道什么,我需要做,使模型线程安全的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 ,你要记录所有的变化_totalPropertyChanged方法。 现在存在一种竞争条件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)
标签: c# wpf mvvm