5

几天来,我一直在用头撞虚拟墙。BindingOperations.EnableSynchronization 方法似乎只在 .NET 4.5 中部分起作用。

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

        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

正如你可以清楚地看到詹妮弗安妮斯顿被列出两次。这可以很容易地复制。这是一个普遍的问题还是这个测试有什么问题,例如有缺陷的应用程序实例化?

先感谢您!

4

3 回答 3

16

该类被记录为不是线程安全的:

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

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

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

由于您实际上并未使用锁定对象,因此您最好不要调用该方法,结果同样不可预测。

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

于 2013-05-12T20:29:38.983 回答
8

我最近也需要解决这个问题,并在 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);
}
于 2015-06-08T19:51:49.293 回答
2

由于您使用的是 .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

于 2013-05-12T20:38:14.373 回答