即使在.net 4.5中ObservableCollection也不是线程安全的

本文关键字:线程 安全 ObservableCollection net | 更新日期: 2023-09-27 18:13:09

这几天我一直在埋头苦干。BindingOperations。在。net 4.5中,enablesynsynchronization方法似乎只能部分工作。

我写了一个有时会失败的测试:

        object blah = new object();
        Application app = Application.Current == null ? new Application() : Application.Current;
        SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
        ObservableCollection<ThreadSafeObservableTestObject> collection = null;
        collection = new ObservableCollection<ThreadSafeObservableTestObject>();
        BindingOperations.EnableCollectionSynchronization(collection, blah);
        CollectionTestWindow w = new CollectionTestWindow();
        Task.Factory.StartNew(() =>
        {
            Thread.Sleep(2000);
            w.TestCollection = collection;
            collection.CollectionChanged += collection_CollectionChanged;
            collection.Add(new ThreadSafeObservableTestObject() { ID = 1, Name = "Sandra Bullock" });
            collection.Add(new ThreadSafeObservableTestObject() { ID = 2, Name = "Jennifer Aniston" });
            collection.Add(new ThreadSafeObservableTestObject() { ID = 3, Name = "Jennifer Lopez" });
            collection.Add(new ThreadSafeObservableTestObject() { ID = 4, Name = "Angelina Jolie" });
            collection.Add(new ThreadSafeObservableTestObject() { ID = 5, Name = "Mary Elizabeth Mastrantonio" });
            Thread.Sleep(5000);
            System.Windows.Application.Current.Dispatcher.Invoke(() => w.Close());
            System.Windows.Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
        });
        app.Run(w);

TestCollectionWindow是这样的:

    <ItemsControl ItemsSource="{Binding TestCollection}" Name="list">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Name}" />
                    <TextBlock Text="{Binding ID}" />
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

没有什么神奇的。但结果是,几乎每次一些条目在UI中出现两次——都是相同的对象!结果窗口看起来像这样:

桑德拉·布洛克1
詹妮弗·安妮斯顿2
詹妮弗·洛佩兹3
安吉丽娜·朱莉4
玛丽·伊丽莎白·马斯特拉安东尼奥5
詹妮弗·安妮斯顿2

你可以清楚地看到Jennifer Aniston被列了两次。这可以很容易地复制。这是一个普遍的问题,还是这个测试有什么问题,比如有缺陷的应用程序实例化?

提前感谢!

即使在.net 4.5中ObservableCollection也不是线程安全的

这个类不是线程安全的:

线程安全
这种类型的任何公共静态(在Visual Basic中共享)成员都是线程安全的。不能保证任何实例成员都是线程安全的。

所以,它不是线程安全的。

注意BindingOperations。EnableCollectionSynchronization并不能神奇地使整个集合线程安全。它只告诉绑定系统你打算使用哪个锁定对象,以防止多个线程同时访问集合。

由于实际上没有使用锁定对象,因此不妨不调用该方法,因为结果同样不可预测。

尝试在访问集合的每个语句周围的blah对象上发出lock。不幸的是,我不知道WPF中数据绑定的细节,所以我不知道这是否足够。

我最近也需要解决这个问题,并在CodeProject上写下了我的解决方案:http://www.codeproject.com/Tips/998619/Thread-Safe-ObservableCollection-T

解决方案包括使用SyncronizationContext来调用UI线程上的事件处理程序和ReaderWriterLockSlim来确保一次只发生一次写操作,并且在读取过程中不发生写操作。

完整的源代码可在上面的CodeProject链接中获得,但这里有一些片段:

public SynchronizedObservableCollection()
{
    _context = SynchronizationContext.Current;
}
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
    var collectionChanged = CollectionChanged;
    if (collectionChanged == null)
    {
        return;
    }
    using (BlockReentrancy())
    {
        _context.Send(state => collectionChanged(this, e), null);
    }
}
public bool Contains(T item)
{
    _itemsLock.EnterReadLock();
    try
    {
        return _items.Contains(item);
    }
    finally
    {
        _itemsLock.ExitReadLock();
    }
}
public void Add(T item)
{
    _itemsLock.EnterWriteLock();
    var index = _items.Count;
    try
    {
        CheckIsReadOnly();
        CheckReentrancy();
        _items.Insert(index, item);
    }
    finally
    {
        _itemsLock.ExitWriteLock();
    }
    OnPropertyChanged("Count");
    OnPropertyChanged("Item[]");
    OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}

因为你使用的是。net 4.5,你可以使用线程安全的集合,ConcurrentDictionary, ConcurrentBag等,根据你的需要:http://msdn.microsoft.com/en-us/library/dd997305.aspx

本文也可能有所帮助:http://www.codeproject.com/Articles/208361/Concurrent-Observable-Collection-Dictionary-and-So