EnableCollectionSynchronization 如何工作?

How does EnableCollectionSynchronization work?

有几篇文章解释了 BindingOperations.EnableCollectionSynchronization 的用法。例如。 BindingOperations.EnableCollectionSynchronization mystery in WPF or Using 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

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

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

的调用
lock (itemsLock)
    items.Add("foo");

documentation 对此非常清楚:

To use a collection on multiple threads, one of which is the UI thread that owns the ItemsControl, an application has the following responsibilities:

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