首页 > 解决方案 > EnableCollectionSynchronization 如何工作?

问题描述

有几篇文章解释了 BindingOperations.EnableCollectionSynchronization 的用法。例如WPF 中的 BindingOperations.EnableCollectionSynchronization 之谜使用 BindingOperations.EnableCollectionSynchronization

但是,我对“锁”的理解与以下演示的行为不符。

private void Button_Click(object sender, RoutedEventArgs e)
{
    var itemsLock = new object();
    var items = new ObservableCollection<string>();
    BindingOperations.EnableCollectionSynchronization(items, itemsLock);

    Task.Run(() =>
    {
        lock (itemsLock)
        {
            Debug.WriteLine("task inside lock");
            Thread.Sleep(5000);
            items.Where(m => m == "foo").ToArray();
        }
        Debug.WriteLine("task outside lock");
    });

    Thread.Sleep(1000);
    Debug.WriteLine("UI thread add..");
    items.Add("foo");
    Debug.WriteLine("UI thread add..done");
}

由于锁定,我希望调试输出是这样的:

task inside lock
UI thread add..
task outside lock
UI thread add..done

但我发现这样的调试输出:

task inside lock
UI thread add..
UI thread add..done
task outside lock

背景信息:在频繁更改的 ObservableCollection 上运行 LINQ 查询时,我偶尔会遇到 InvalidOperationExceptions“集合已修改”。这导致我将其分解为上一个示例。然后我发现我对 EnableCollectionSynchronization 工作原理的假设是错误的。

标签: c#wpfobservablecollection

解决方案


您应该使用相同的锁同步对集合的所有访问,即您应该锁定调用Add

lock (itemsLock)
    items.Add("foo");

文档对此非常清楚:

要在多个线程(其中一个是拥有 的 UI 线程)上使用集合,ItemsControl应用程序具有以下职责:

  • 选择一种同步机制。
  • 使用该机制同步从应用程序到集合的所有访问。
  • 调用EnableCollectionSynchronization以通知 WPF 该机制。
  • ...

推荐阅读