EnableCollectionSynchronization 如何工作?

sa.*_*.he 3 c# wpf observablecollection

有几篇文章解释了 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");
}
Run Code Online (Sandbox Code Playgroud)

由于锁定,我预计调试输出如下:

task inside lock
UI thread add..
task outside lock
UI thread add..done
Run Code Online (Sandbox Code Playgroud)

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

task inside lock
UI thread add..
UI thread add..done
task outside lock
Run Code Online (Sandbox Code Playgroud)

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

mm8*_*mm8 6

您应该使用同一锁同步对集合的所有Add访问,即您应该锁定对以下内容的调用:

lock (itemsLock)
    items.Add("foo");
Run Code Online (Sandbox Code Playgroud)

文档对此非常清楚

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

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

  • 我真丢脸——我一定是瞎了眼。不知何故,我认为 BindingOperations.EnableCollectionSynchronization 使我的 ObservableCollection 线程安全。然而,它所做的只是与 WPF 绑定共享锁定对象。 (3认同)