Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#线程安全(特别是MVVM/WPF)_C#_Wpf_Mvvm - Fatal编程技术网

C#线程安全(特别是MVVM/WPF)

C#线程安全(特别是MVVM/WPF),c#,wpf,mvvm,C#,Wpf,Mvvm,我想知道我需要做些什么来确保MVVM中的模型线程安全。假设我有以下类,它被实例化为单例: public class RunningTotal: INotifyPropertyChange { private int _total; public int Total { get { return _total; } set { _total = value; PropertyChanged("Total");

我想知道我需要做些什么来确保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}

我的应用程序有一个后台线程,定期更新Total的值。假设没有其他更新,我应该做什么(如果有的话)来保证所有线程的安全


现在,如果我想做一些类似的事情,但使用类型为
Dictionary
observetecollection
的属性,该怎么办?哪些成员(添加、删除、清除、索引器)是线程安全的?我应该改用ConcurrentDictionary吗?

模型应该像任何代码一样以线程安全的方式编写;由您决定是否使用锁、并发容器或任何其他方法来执行此操作。这些模型只是库代码,它(几乎)不应该知道MVVM应用程序将使用它的功能

然而,虚拟机必须在UI线程中工作。这意味着他们通常不能依赖于来自模型的事件是否来自UI线程,因此如果订阅的事件不来自UI线程,他们必须封送调用或将其存储在任务队列中

因此,VM应该以一种特定的方式关注线程安全,而不是模型所需要的

反过来,视图代码通常可以愉快地忽略所有线程问题:它获取专用UI线程中的所有消息/调用/事件/任何内容,并在UI线程中进行自己的调用



特别是对于您的情况,您的代码不是模型而是虚拟机,对吗?在这种情况下,您必须在UI线程中触发事件,否则视图将不满意。

假设有两个线程正在更新
Total
,并且您希望在
PropertyChanged
方法中记录对
\u Total
的所有更改。现在存在一种竞争条件,
PropertyChanged
可能会丢失一个值。当一个线程在调用<代码> SETIONTROUT> /COD>中间时发生这种情况。它更新了
\u total
,但仍然调用PropertyChanged。同时,另一个线程将
\u total
更新为另一个值:

thread1: _total = 4;
thread2: _total = 5;
thread2: PropertyChanged("Total");
thread1: PropertyChanged("Total");
现在,
PropertyChanged
的值永远不会被调用为4

您可以通过将值传递给
PropertyChanged
方法或使用setter中的锁来解决此问题

因为您说您有一个线程更新这个属性,所以不可能存在争用条件。只有当多个线程(或进程)同时更新同一事物时,才会出现这种情况

我的应用程序有一个后台线程,定期更新Total的值。假设没有其他更新,我应该做什么(如果有的话)来保证所有线程的安全

对于标量属性,您不需要做任何特殊的事情;
PropertyChanged
事件将自动封送到UI线程

现在,如果我想做类似的事情,但使用Dictionary或ObservableCollection类型的属性,该怎么办?哪些成员(添加、删除、清除、索引器)是线程安全的?我应该改用ConcurrentDictionary吗

不,这不是线程安全的。如果从后台线程更改
ObservableCollection
的内容,它将中断。您需要在UI线程上执行此操作。一种简单的方法是使用在UI线程上引发其事件的集合,如所述


至于
字典
,当其内容发生更改时,它不会发出通知,因此用户界面无论如何都不会收到通知。

这个问题提供了ObservableCollection的线程封送版本

但是,您仍然需要担心线程之间的争用,这需要您在更新资源时锁定资源,或者使用类似Interlocked.Increment的东西

如果一个线程正在更新,而另一个线程正在读取,则有可能在更新的中途执行读取(例如,正在修改Int64。前半部分(32位)已在一个线程中更新,在后半部分更新之前,从第二个线程读取该值。读取的值完全错误)


这可能是问题,也可能不是问题,这取决于应用程序将要执行的操作。如果错误的值将在GUI上闪烁1秒,那么这可能不是什么大问题,锁的性能损失可以忽略。如果您的程序将根据该值执行操作,那么您可能需要将其锁定

一个简单的答案是,您需要通过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 */ }

一些更详细的东西,其实很简单。。。在视图中实例化viewmodel时,只需在ctor中向下传递调度程序

    ServerOperationViewmodel ViewModel;        
    public pgeServerOperations()
    {
        InitializeComponent();
        ViewModel = new ServerOperationViewmodel(Dispatcher);
    }
然后在视图模型中:

Dispatcher UIDispatcher;
public ServerOperationViewmodel(Dispatcher uiDisp)
{
    UIDispatcher = uiDisp;
}
并像普通的UI调度器一样使用它

UIDispatcher.Invoke(() =>
{
  .......
});

我承认我对MVVM还是相当陌生的,但我不认为这违背了MVVM的座右铭

另请参见:,尝试以下链接,该链接提供了一个线程安全的ObservableCollection类型解决方案,该解决方案可从任何线程运行,并可通过多个UI线程绑定到:
PropertyChanged
如何自动封送到UI线程?魔法在哪里?我希望事件发生在它触发的上下文中。@Vlad,“魔力”在绑定引擎中。
PropertyChanged
事件本身可能会在后台线程上触发,但绑定引擎会在UI线程上对其作出反应(使用Dispatcher.Invoke)@thomaslevsque这对我来说是新闻,你能提供一些文档吗?@Coding Gorilla:看到了吗。这证实了Thomas的说法,即bi