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);
                }
        }

 

 

using MaterialDesignThemes.Wpf; using Motion_YC.Common.Enums; using Motion_YC.Common.Provide; using Motion_YC.Help; using Motion_YC.Motion; using Motion_YC.Sensor; using Motion_YC.ViewModels; using Motion_YC.Views; using Newtonsoft.Json; using ScottPlot; using System; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows.Threading; namespace Motion_YC.Models { public class MainViewModel : NotifyPropertyBase { ///单例模式 private static MainViewModel _Instace = new MainViewModel(); private MainViewModel() { _timer = new DispatcherTimer(); _timer.Interval = TimeSpan.FromMilliseconds(200); // 统一设置间隔 _timer.Tick += OnTimerTick; // 绑定统一的Tick事件处理 } public static MainViewModel Ins { get { return _Instace; } set { _Instace = value; } } #region Prop [JsonIgnore] public GTS_Aixs8 Motion { get; } = GTS_Aixs8.Ins; public MicroEpsilonSensor Sensor { get; }= MicroEpsilonSensor.Ins; /// <summary> /// 第一次运行位置 /// </summary> private double _StartOnePose; public double OnePose { get { return _StartOnePose; } set { _StartOnePose = value; RaisePropertyChanged();} } /// <summary> /// 第一运行结束位置 /// </summary> private double _EndOnePose; public double EndOnePose { get { return _EndOnePose; } set { Set(ref _EndOnePose,value); } } /// <summary> /// 第二次运行位置 /// </summary> private double _TwoPose; public double TwoPose { get { return _TwoPose; } set { _TwoPose = value; RaisePropertyChanged(); } } private bool _OneRel; public bool OneRel { get { return _OneRel; } set { _OneRel = value; RaisePropertyChanged(); } } private bool _TwoRel; public bool TwoRel { get { return _TwoRel; } set { _TwoRel = value; RaisePropertyChanged(); } } private string LineType; private readonly DispatcherTimer _timer; // 改为类成员,仅创建一次 public ObservableCollection<DataModel> dataModels { get; set; }= new ObservableCollection<DataModel>(); #endregion #region Command private CommandBase _LoadedCommand; [JsonIgnore] public CommandBase LoadedCommand { get { if (_LoadedCommand == null) { _LoadedCommand = new CommandBase((obj) => { }); } return _LoadedCommand; } } private CommandBase _OperateCommand; [JsonIgnore] public CommandBase OperateCommand { get { if (_OperateCommand == null) { _OperateCommand = new CommandBase((obj) => { switch (obj) { case "OneStart": LineType = "SeriesA"; Motion.MoveAbs(OnePose); StartDataUpdate(); // 启动数据更新逻辑 break; case "OneEnd": Motion.MoveAbs(EndOnePose); break; case "Enable": Motion.Enable(); break; case "Disable": Motion.Disable(); break; case "TargetPose": LineType = "SeriesB"; Motion.MoveAbs(TwoPose); StartDataUpdate(); // 启动数据更新逻辑 break; case "Stop": Motion.MoveStop(); break; case "ClearAlm": Motion.ClearAlm(); break; case "ZeroPose": Motion.ClearPos(1); break; case "CoordinateClear": dataModels.Clear(); break; } }); } return _OperateCommand; } } #endregion private void OnTimerTick(object? sender, EventArgs e) { // 向集合添加新数据(UI线程安全) dataModels.Add(new DataModel { Pose = Motion.CurrentPosition, Value = 500, SeriesName = LineType, }); // 限制数据量:超过5000条时移除最旧数据 if (dataModels.Count > 300) { dataModels.RemoveAt(0); // 移除第一条(最旧) } if (!Motion.IsMoving) { StopDataUpdate(); } } private void StartDataUpdate() { if (_timer.IsEnabled && Motion.IsMoving) return; // 避免重复启动 _timer.Start(); // 启动定时器 } private void StopDataUpdate() { if (_timer.IsEnabled) { _timer.Stop(); // 停止定时器 // 可选:重置计数器(根据需求决定是否保留历史值) // _i = 0; // _j = 0; } } } } 分析为什么EndOnePose在反序列化时已经将序列化之前100通过SET赋值了,界面为什么没更新
最新发布
08-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值