我要创建一个WPF UI,这subsribes实时外汇汇率(货币+速度)的更新,并将其显示在网格(每秒大约1000次更新,这意味着网格中的每个行可能获得更新高达每秒1000次) .网格在任何时间点都至少有50行.
为此,我创建了一个Viewmodel,它订阅更新事件,并将这些更新存储在并发字典中,其中key作为符号,值作为RateViewModel对象.然后我有另一个可观察的集合,它包含所有那些rateviewmodel对象,并将其绑定到网格.
码:
public class MyViewModel
{
private readonly IRatesService ratesService;
private readonly ConcurrentDictionary<string, RateViewModel> rateDictionary;
private object _locker = new object();
public MyViewModel(IRatesService ratesService)
{
this.ratesService = ratesService;
this.ratesService.OnUpdate += OnUpdate;
rateDictionary = new ConcurrentDictionary<string, RateViewModel>();
RateViewModels = new ObservableCollection<RateViewModel>();
}
private void OnUpdate(object sender, RateUpdateEventArgs e)
{
RateViewModel exisistingRate;
if (!rateDictionary.TryGetValue(e.Update.Currency, out exisistingRate))
{
exisistingRate = new RateViewModel(new Rate(e.Update.Currency, e.Update.Rate));
rateDictionary.TryAdd(e.Update.Currency, exisistingRate);
return;
}
lock (_locker)
{
exisistingRate.UpdateRate(e.Update.Rate);
}
Application.Current.Dispatcher.BeginInvoke(new Action(() => SearchAndUpdate(exisistingRate)));
}
public ObservableCollection<RateViewModel> RateViewModels { get; set; }
private void SearchAndUpdate(RateViewModel rateViewModel)
{
//Equals is based on Currency
if (!RateViewModels.Contains(rateViewModel))
{
RateViewModels.Add(rateViewModel);
return;
}
var index = RateViewModels.IndexOf(rateViewModel);
RateViewModels[index] = rateViewModel;
}
}
Run Code Online (Sandbox Code Playgroud)
我有4个问题:
有没有办法可以消除ObservableCollection,因为它导致两个不同的数据结构存储相同的项目 - 但仍然将我的更新中继到UI?
我使用了Concurrent Dictionary,它导致锁定整个更新操作.是否有任何其他聪明的方法来处理这个而不是锁定整个dicitionary或任何数据结构?
我的UpdateRate方法也会锁定 - 我的RateviewModel上的所有属性都是只读的,除了价格,因为它正在更新.有没有办法使这个原子,请注意价格是双倍的.
有没有办法可以优化SearchAndUpdate方法,这与1st相关.目前我认为这是O(n)操作.
使用.NET 4.0并省略了INPC以简化.
*编辑:*你可以帮助我以更好的方式重写这一点,考虑所有4点吗?Psuedocode会这样做.
谢谢,-Mike
1) 我不会担心 50 个额外的裁判
2)是的,无锁数据结构是可行的。 联锁是你的朋友在这里,他们几乎都是一次性的。如果您不经常更改字典中的项目,那么ReaderWriterLock是另一个不错的选择。
3) 一般来说,如果您要处理的数据多于 UI 可以处理的数据,您将需要在后台进行更新,仅在 UI 线程上触发 INPC,更重要的是具有删除 UI 更新的功能(同时仍在更新支持字段)。基本方法如下:
Interlocked.Exchange在后场做一个Interlocked.CompareExchange将私有字段设置为 1,如果这返回 1 退出,因为仍有待处理的 UI 更新Interlocked.CompareExchange返回 0,则调用 UI 并触发属性更改事件并将限制字段更新为 0(从技术上讲,如果您关心非 x86,则需要做更多事情)4)SearchAndUpdate似乎多余......UpdateRate应该冒泡到UI,并且如果您需要向可观察集合添加或删除项目,则只需要调用UI线程。
更新:这是一个示例实现...事情稍微复杂一点,因为您使用的是双精度数,它在 32 位 CPU 上无法免费获得原子性。
class MyViewModel : INotifyPropertyChanged
{
private System.Windows.Threading.Dispatcher dispatcher;
public MyViewModel(System.Windows.Threading.Dispatcher dispatcher)
{
this.dispatcher = dispatcher;
}
int myPropertyUpdating; //needs to be marked volatile if you care about non x86
double myProperty;
double MyPropery
{
get
{
// Hack for Missing Interlocked.Read for doubles
// if you are compiled for 64 bit you should be able to just do a read
var retv = Interlocked.CompareExchange(ref myProperty, myProperty, -myProperty);
return retv;
}
set
{
if (myProperty != value)
{
// if you are compiled for 64 bit you can just do an assignment here
Interlocked.Exchange(ref myProperty, value);
if (Interlocked.Exchange(ref myPropertyUpdating, 1) == 0)
{
dispatcher.BeginInvoke(() =>
{
try
{
PropertyChanged(this, new PropertyChangedEventArgs("MyProperty"));
}
finally
{
myPropertyUpdating = 0;
Thread.MemoryBarrier(); // This will flush the store buffer which is the technically correct thing to do... but I've never had problems with out it
}
}, null);
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate {};
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2286 次 |
| 最近记录: |