实时更新 UI

2024-01-07

我必须创建一个 WPF UI,它订阅实时外汇汇率(货币 + 汇率)更新并将其显示在网格中(每秒大约 1000 次更新,这意味着网格中的每一行都可以更新upto每秒 1000 次)。网格在任何时间点都至少有 50 行。

为此,我创建了一个订阅更新事件的 Viewmodel,并将这些更新存储在并发字典中,其中键作为符号,值作为 RateViewModel 对象。然后我有另一个可观察的集合,其中包含所有这些rateviewmodel 对象,并将其绑定到网格。

Code:

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

我对此有4个问题:

  • 有没有一种方法可以消除 ObservableCollection,因为它会导致 2 个不同的数据结构存储相同的项目 - 但仍然将我的更新转发到 UI?

  • 我使用了并发字典,这导致锁定整个更新操作。除了锁定整个字典或任何数据结构之外,还有其他聪明的方法来处理这个问题吗?

  • 我的 UpdateRate 方法也锁定 - 我的 RateviewModel 上的所有属性都是只读的,除了价格之外,因为它正在更新。有没有办法使这个原子化,请注意,价格是双倍的。

  • 有没有办法可以优化 SearchAndUpdate 方法,这与第一个相关。目前我相信这是一个 O(n) 操作。

Using .NET 4.0为简洁起见,省略了 INPC。

*EDIT:*您能否帮我以更好的方式重写此内容,同时考虑到所有 4 点?伪代码就可以了。

谢谢, -麦克风


1) 我不会担心 50 个额外的裁判

2)是的,无锁数据结构是可行的。联锁 http://msdn.microsoft.com/en-us/library/system.threading.interlocked.aspx你的朋友在这里吗?他们几乎都是一次性的。读写锁 http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlock.aspx如果您不经常更改字典中的项目,那么这是另一个不错的选择。

3) 一般来说,如果您要处理的数据多于 UI 可以处理的数据,您将需要在后台进行更新,仅在 UI 线程上触发 INPC,更重要的是具有删除 UI 更新的功能(同时仍在更新支持字段)。基本方法如下:

  1. Do an Interlocked.Exchange在支持场上
  2. Use Interlocked.CompareExchange将私有字段设置为 1,如果返回 1 则退出,因为仍有待处理的 UI 更新
  3. If 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 {};


}    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

实时更新 UI 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • C# 动态/expando 对象的深度/嵌套/递归合并

    我需要在 C 中 合并 2 个动态对象 我在 stackexchange 上找到的所有内容仅涵盖非递归合并 但我正在寻找能够进行递归或深度合并的东西 非常类似于jQuery 的 extend obj1 obj2 http api jquer
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • C++ 继承的内存布局

    如果我有两个类 一个类继承另一个类 并且子类仅包含函数 那么这两个类的内存布局是否相同 e g class Base int a b c class Derived public Base only functions 我读过编译器无法对数
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使

随机推荐