WPF中一个非常强大的功能是数据绑定,我们可以把一个集合绑定到ListBox中,当集合的数据发生变更时,ListBox界面也会同步变更。本身这是一个非常美好的事情,但是美中不足的是:当把集合绑定到ListBox中的时候,集合也顺带继承了ListBox的这种不能夸线程访问的限制。例如,如下代码就会抛出跨线程访问异常。
解决这个问题的一个传统方式是把对集合的修改Post到UI线程中来,改成如下形式。
ThreadPool.QueueUserWorkItem(async _ =>
{
await Task.Delay(1000);
this.Dispatcher.Invoke(new Action(()=>collection.Add(DateTime.Now.ToString())), null);
});
不过这种写法显得很繁琐,在.Net 4.5提供了新的一种线程安全访问机制,那就是用BindingOperations.EnableCollectionSynchronization使能集合同步:
var collection = new ObservableCollection<string>();
listBox.ItemsSource = collection;
var lockObj = new object();
BindingOperations.EnableCollectionSynchronization(collection, lockObj);
ThreadPool.QueueUserWorkItem(async _ =>
{
await Task.Delay(1000);
collection.Add(DateTime.Now.ToString());
});
这样,对集合访问就没有UI线程限制了,要方便不少。没有细研究其内部实现机制,貌似是通过加锁实现的。
与之相对的是,还提供了一个去使能集合同步的函数BindingOperations.DisableCollectionSynchronization。网上的文章说是使用完后要用这个函数去使能集合同步,否则会以为集合的引用没有释放导致内存泄漏。不过,我自己写代码试了一下,即使不用它解除锁定,集合对象还是能正常释放的,应该保存的只是一个弱引用,不主动解除锁定也没有内存泄漏问题。