ObservableCollection 集合的线程不安全导致的数据重复错误及解决办法。

在实际项目的线程运作中,采集数据时虽判断数据重复,但仍出现重复现象。介绍了Lock方法,对保存数据的集合ObservableCollection使用lock防止篡改和内部计算问题,还给出替代办法,如AsyncObservableCollection可替代ObservableCollection。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   

 实际项目中,应用到了线程运作:

    如采集一个数据调用CatchDataByDay方法:

          Task.Run(() => CatchDataByDay());

    虽然利用代码2,判断数据是否重复。意图只保存不重复数据。但仍出现重复现象。

 

  一、Lock的方法
 

    1.保存数据的集合 ObservableCollection

       private ObservableCollection<Race> _RaceCollect;
        public ObservableCollection<Race> RaceCollect   //用于保存数据的集合,并向UI通知更新
        {
            get { return this._RaceCollect; }
            set
            {
                this._RaceCollect = value;
                RaisePropertyChanged(() => RaceCollect);
            }
        }

    代码1:如下代码向RaceCollect集合中添加新数据,为防止集合被篡改,所以此处使用了lock(object)

 private void CatchDataByDay()
        {
             //此处省无关代码。

                string rsp = _http.HttpGet(url);

                JavaScriptSerializer serializer = new JavaScriptSerializer();
                var raceObject = serializer.Deserialize<Raceobject>(rsp);
                DataItem[] datas = raceObject.result.data;
                int n;
                for (int i = datas.Length - 1; i >= 0; i--)
                {
                    var a = datas[i];

                    //判断是否存在该数据
                    if (ListContains(a.preDrawIssue)) continue;
                    //
                    Race race = new Race();
                     
                    //此处省无关代码。

                    lock (locker)
                    {
                        _dispatcher.Invoke(new Action(() =>{_RaceCollect.Add(race);}));

                        PickNumbersFromCollection();//挑选顺序出现的数字,并判断是否达到10个
                    }

                }
            }//day
            //此处省无关代码。
        }

 代码2:_RaceCollect也进行了lock锁定,防止操作 List 时,内部计算可能会出现问题

 //判断集合中是否存在该数据
        private bool ListContains(string qh)
        {
            lock (locker)
            {
                var races = _RaceCollect.ToList<Race>();
                var ar = (from x in races where (x.QH.ToString().Trim() == qh.Trim()) select x).ToArray();
                if (ar == null || ar.Count() == 0)
                    return false;
                else return true;
            }
            // 异常:System.ArgumentException: 目标数组的长度不够。请检查 destIndex 和长度以及数组的下限
            // 原因:List<T> 集合不是线程安全的,在并发操作 List 时,内部计算可能会出现问题

        }

二、其他解决办法

1。替代方法1

AsyncObservableCollection 继承自ObservableCollection类,可替代其使用。

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Threading;

namespace Airship
{
    /// <summary>
    /// 线程安全的集合,将ObservableCollection替换掉即可
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class AsyncObservableCollection<T> : ObservableCollection<T>
    {
        //获取当前线程的SynchronizationContext对象
        private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;
        public AsyncObservableCollection() { }
        public AsyncObservableCollection(IEnumerable<T> list) : base(list) { }
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {

            if (SynchronizationContext.Current == _synchronizationContext)
            {
                //如果操作发生在同一个线程中,不需要进行跨线程执行         
                RaiseCollectionChanged(e);
            }
            else
            {
                //如果不是发生在同一个线程中
                //准确说来,这里是在一个非UI线程中,需要进行UI的更新所进行的操作         
                _synchronizationContext.Post(RaiseCollectionChanged, e);
            }
        }
        private void RaiseCollectionChanged(object param)
        {
            // 执行         
            base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
        }
        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (SynchronizationContext.Current == _synchronizationContext)
            {
                // Execute the PropertyChanged event on the current thread             
                RaisePropertyChanged(e);
            }
            else
            {
                // Post the PropertyChanged event on the creator thread             
                _synchronizationContext.Post(RaisePropertyChanged, e);
            }
        }
        private void RaisePropertyChanged(object param)
        {
            // We are in the creator thread, call the base implementation directly         
            base.OnPropertyChanged((PropertyChangedEventArgs)param);
        }
    }
}

2。替代2

/// <summary>
    /// 如何使的 ObservableCollection 线程安全的?(How to make ObservableCollection thread-safe?)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MTObservableCollection<T> : ObservableCollection<T>
    {
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;

            if (CollectionChanged != null)
                foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
                {
                    DispatcherObject dispObj = nh.Target as DispatcherObject;
                    if (dispObj != null)
                    {
                        Dispatcher dispatcher = dispObj.Dispatcher;
                        if (dispatcher != null && !dispatcher.CheckAccess())
                        {
                            dispatcher.BeginInvoke(
                                (Action)(() => nh.Invoke(this,
                                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
                                DispatcherPriority.DataBind);
                            continue;
                        }
                    }
                    nh.Invoke(this, e);
                }
        }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值