使用 Rx 和 ObservableCollection 实现线程安全



为了追求更灵敏的方式来更新具有大量项目的ListBox,我转向了Rx。这是我的实现:

    ObservableCollection<FileData> _fileCollection = new ObservableCollection<FileData>();
    public ObservableCollection<FileData> FileCollection { get { return _fileCollection; } }
    public static object _fileCollectionLock = new object();
    public Subject<FileData> subject = new Subject<FileData>();
    public MainWindow( )
    {
        InitializeComponent();
        BindingOperations.EnableCollectionSynchronization(_fileCollection, _fileCollectionLock);
        UpdateFileList(subject);
    }
    private void UpdateFileList(IObservable<FileData> sequence)
    {
        sequence.Subscribe(value=>_fileCollection.Add(value));
    }
    private void ListFiles(string fullpath)
    { 
        _fileCollection.Clear(); //crashed once
        Task.Factory.StartNew(() =>
        {
            DirectoryInfo info = new DirectoryInfo(fullpath);
            IEnumerable files = info.EnumerateFiles(Filter + "*", SearchOption.TopDirectoryOnly,true);
            foreach (FileInfo file in files)
            {
                ...
                FileData fd = new FileData(filename, filedate, filesize, fileext);      
                subject.OnNext(fd);

曾经在我的代码中崩溃了 _fileCollection.Clear();(忘记错误,抱歉)。我需要使用锁吗,在哪里?

您的实现存在几个问题,根源在于对如何正确使用 EnableCollectionSynchronization 的误解。老实说,我并不感到惊讶 - 它的文档很差,设计也不是很好(我怀疑主要是因为必须让它与 WPF 一起工作而不会中断更改)。

正确使用EnableCollectionSynchronization

我将简要介绍正确的用法:

EnableCollectionSynchronization注册一个锁,WPF 将在需要访问集合时(例如,当控件枚举集合时)使用该锁。

在 .NET 4.5 中,在后台线程(即不是从何处调用EnableCollectionSynchronization的线程的线程)上引发的集合更改事件无论如何都会排队并封送到 UI 线程。

重要的是,使用此方法时,您仍然必须使用您在EnableCollectionSynchronization中注册的相同锁自己锁定对集合的任何访问。特别是,代码中没有任何内容阻止Clear()Add()同时运行,这肯定是导致您看到的异常的原因。

调用EnableCollectionSynchronization的好位置是从处理程序到 BindingOperations.CollectionRegistering 事件,这保证在创建任何CollectionView实例之前进行调用。

更新 UI 绑定集合的最佳做法

综上所述,我认为您应该完全放弃这种方法 - 最好只更新 UI 线程上的 UI 绑定集合。RX 非常适合此,您可以使用ObserveOn(请参阅此处以获取良好的入门)来编组对 UI 线程的更新。如果您这样做,则不再需要担心同步对集合的访问,您的生活将变得更加简单。

我的建议 - 完成所有工作,以确定后台线程上需要哪些更新,但修改 UI 线程本身的集合。这种方法几乎总是足够快,如果不是,您可能需要考虑您的设计。

请在此处查看我的回答以进行讨论,并链接到有关UI更新的文章。

另请查看此问题,以再次讨论/分析UI线程上发生大量更改的问题,以及如何缓冲大量更改以避免占用调度程序。

ReactiveUI框架也值得一看。另请参阅此处。

相关内容

  • 没有找到相关文章

最新更新