我现在正在撞击虚拟墙几天.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);
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
所以没有什么神奇的.但结果几乎每次都有一些条目在UI中两次 - 相同的对象!结果窗口如下所示:
Sandra Bullock 1
Jennifer Aniston 2
Jennifer Lopez 3
Angelina Jolie 4
Mary Elizabeth Mastrantonio 5
Jennifer Aniston 2
你可以清楚地看到Jennifer Aniston被列入两次.这可以很容易地再现.这是一个普遍的问题,还是这个测试有什么问题,例如应用程序实例化有缺陷?
先感谢您!
ang*_*son 13
记录该类不是线程安全的:
线程安全
此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的.任何实例成员都不保证是线程安全的.
所以不,它不是线程安全的.
请注意,BindingOperations.EnableCollectionSynchronization不会神奇地使整个集合成为线程安全的.它只告诉绑定系统您打算使用哪个锁定对象,以防止多个线程同时访问该集合.
由于您实际上并未使用锁定对象,因此您也可以不调用该方法,结果将同样无法预测.
尝试lock在blah访问集合的每个语句周围发出一个对象.不幸的是,我不了解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);
}
Run Code Online (Sandbox Code Playgroud)