简体   繁体   English

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

[英]ObservableCollection not thread-safe even in .NET 4.5?

I am banging my head against the virtual wall for days now. 我现在正在撞击虚拟墙几天。 The BindingOperations.EnableSynchronization method seems to work only partial in .NET 4.5. BindingOperations.EnableSynchronization方法似乎只在.NET 4.5中起作用。

I wrote a test that fails sometimes: 我写了一个有时失败的测试:

        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);

The TestCollectionWindow looks like this: 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>

So nothing magic here. 所以没有什么神奇的。 But the result is almost every time that some entries are twice in the UI - the same objects! 但结果几乎每次都有一些条目在UI中两次 - 相同的对象! The result window looks like this then: 结果窗口如下所示:

Sandra Bullock 1 桑德拉布洛克1
Jennifer Aniston 2 珍妮弗安妮斯顿2
Jennifer Lopez 3 詹妮弗洛佩兹3
Angelina Jolie 4 安吉丽娜朱莉4
Mary Elizabeth Mastrantonio 5 Mary Elizabeth Mastrantonio 5
Jennifer Aniston 2 珍妮弗安妮斯顿2

As you can clearly see Jennifer Aniston is listed twice. 你可以清楚地看到Jennifer Aniston被列入两次。 This can be reproduced easily. 这可以很容易地再现。 Is this a general problem or is there anything wrong with this test, such as a flawed application instantiation? 这是一个普遍的问题,还是这个测试有什么问题,例如应用程序实例化有缺陷?

Thank you in advance! 先感谢您!

The class is documented to not be thread-safe: 记录该类不是线程安全的:

Thread Safety 线程安全
Any public static (Shared in Visual Basic) members of this type are thread safe. 此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。 Any instance members are not guaranteed to be thread safe. 任何实例成员都不保证是线程安全的。

So no, it is not thread-safe. 所以不,它不是线程安全的。

Note that BindingOperations.EnableCollectionSynchronization does not magically make the entire collection thread-safe. 请注意, BindingOperations.EnableCollectionSynchronization不会神奇地使整个集合成为线程安全的。 It only tells the binding system which locking object that you intend to use in order to prevent multiple threads accessing the collection at the same time. 它只告诉绑定系统您打算使用哪个锁定对象,以防止多个线程同时访问该集合。

Since you're not actually using the locking object, you might as well not call that method, the results will be equally unpredictable. 由于您实际上并未使用锁定对象,因此您也可以不调用该方法,结果将同样无法预测。

Try issuing a lock on the blah object around each statement that accesses the collection. 尝试在访问集合的每个语句周围对blah对象发出lock Unfortunately the details around data binding in WPF is unknown to me, so I have no idea if that is enough. 不幸的是,我不了解WPF中数据绑定的细节,所以我不知道这是否足够。

I recently needed to solve this issue as well and wrote about my solution on CodeProject: http://www.codeproject.com/Tips/998619/Thread-Safe-ObservableCollection-T 我最近也需要解决这个问题,并在CodeProject上写了我的解决方案: http//www.codeproject.com/Tips/998619/Thread-Safe-ObservableCollection-T

The solution involved using a SyncronizationContext to invoke the event handlers on the UI thread and a ReaderWriterLockSlim to ensure only one write occurred at a time and that no writes occurred during a read. 该解决方案涉及使用SyncronizationContext调用UI线程上的事件处理程序和ReaderWriterLockSlim,以确保一次只发生一次写入,并且在读取期间没有发生写入。

Full source code is available at the CodeProject link above but here's some snippets: 上面的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);
}

Since you're using .Net 4.5, you can use the Thread-safe collections, ConcurrentDictionary, ConcurrentBag etc, whichever suits your needs : http://msdn.microsoft.com/en-us/library/dd997305.aspx 由于您使用的是.Net 4.5,因此可以使用Thread-safe集合,ConcurrentDictionary,ConcurrentBag等,以满足您的需求: http//msdn.microsoft.com/en-us/library/dd997305.aspx

This article may also help : http://www.codeproject.com/Articles/208361/Concurrent-Observable-Collection-Dictionary-and-So 本文也可以提供帮助: http//www.codeproject.com/Articles/208361/Concurrent-Observable-Collection-Dictionary-and-So

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM