实际项目中,应用到了线程运作:
如采集一个数据调用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);
}
}