【问题标题】:How to Cache Real-time Data?如何缓存实时数据?
【发布时间】:2011-05-10 18:38:24
【问题描述】:
  • 我正在开发一个 Windows 窗体应用程序 (.NET 4.0)。
  • 我的表单包含一个使用 VS2010 中包含的 Microsoft 图表控件的“Fast Line”图表。
  • 图表填充了大约 20,000 个数据点。
  • 然后我的应用程序开始通过 DDE(动态数据交换)从服务器实时接收市场数据并将其添加到图表中。

注意:我无法控制服务器,因此我只能处理 DDE,即使它是一种过时的技术。 VS 不再支持 DDE,所以我使用了 Ndde 库,它就像一个魅力。

首先我们连接到服务器,创建一个通知循环,然后订阅 OnAdvise 事件以接收新数据的通知:

Dim client As DdeClient = New DdeClient("ServerApplication", "Bid")

Private Sub StartDDE()
    client.Connect()
    client.StartAdvise("EURUSD", 1, True, 60000)
    AddHandler client.Advise, AddressOf OnAdvise
End Sub

现在我们可以将更新图表的命令放入事件中:

Private Sub OnAdvise(ByVal sender As Object, ByVal args As DdeAdviseEventArgs)
    Dim myPrice As Double = args.Text
    Chart1.Series("Bid").Points.AddY(myPrice)
End Sub

你明白了。

问题:

这可以正常工作几秒钟,直到图表崩溃并引发异常: "集合已修改;枚举操作可能无法执行。"

我花了很多时间研究在我的特定情况下可能是什么原因,我得出的结论是,这是因为图表接收数据的速度超过了它可以处理的速度。它已经加载了大量数据,需要一定的时间(不到一秒)将接收到的数据添加到新的 DataPoint 中并使其自身失效(刷新)。而服务器通常会非常快速地发送数据值(例如之间的 5 毫秒)。所以我尝试了以下方法:

System.Threading.Thread.Sleep(800)
Chart1.Series("Bid").Points.AddY(myPrice)

因此暂停应用程序以便在添加新点之前让图表有时间完成其工作,你猜怎么着?该应用程序现在可以在引发异常之前运行几分钟。 (但是,更改 Sleep() 中的值并没有任何帮助)

我能在网上找到的唯一帮助是一篇旧帖子,有人提到您应该将传入数据放在缓存队列中,一次从现金中释放一个新数据值(每次图表完成工作时)。

我的问题是你会怎么做?

欢迎提出其他建议!

【问题讨论】:

  • 您是否有机会从非 UI 线程更新图表数据集合?
  • 这听起来确实像一个线程问题 - 有东西试图在枚举时修改集合。
  • OnAdvise 是一个事件委托函数,对吧?尝试调用方法 AddY 而不是执行。 (或者更好的是,看看 InvokeRequired 值。假设这个值是真的,我认为是的,你不能在不调用它的情况下安全地从该函数调用它(将数据同步传递给另一个线程))。但是,需要做的是将添加的点保留在结构中,并在停止迭代数据点后将它们全部添加。然后在迭代时不会更改集合,并且下一次将具有更新的数据。
  • @Gabe & rsbarro:是的,看起来是这样。 @Derk-Jan Karrenbeld:感谢您的建议,我正在研究它们。

标签: vb.net .net-4.0 mschart dde


【解决方案1】:

这很可能是由于尝试从 UI 线程以外的线程修改 UI 元素而导致的问题。

按照您现在的编码方式,DdeClient.Advise 事件处理程序正在库管理的工作线程上执行。看,DDE 很烂,因为它很烂,所以它有这些要求,它必须在带有消息泵的线程上运行。1 为了使库与除 windows 窗体之外的其他类型的应用程序兼容,我将其编码为这样,它将创建一个带有消息循环的专用线程,并默认将所有操作编组到该线程上。

但是,您可以通过在 DdeClient 构造函数中手动指定 ISynchronizeInvoke 实例来覆盖此行为。然后,该库将使用托管 ISynchronizeInvoke 实例的任何线程来执行其所有 DDE 操作。所有FormControl 实例都实现ISynchronizeInvoke,因此很容易告诉库使用主UI 线程。

Dim client As DdeClient = New DdeClient("ServerApplication", "Bid", yourForm)

如果您告诉库使用您的Form 实例,那么Advise 事件处理程序将在托管Form 的同一线程上执行; UI 线程。

顺便说一句,我意识到您无法控制服务器,但我至少会开始与软件供应商讨论,以使用更现代(不是 20 年前)的机制进行进程间通信。


1不幸的是,它还需要线程亲和性,这使得处理垃圾收集器非常痛苦。

【讨论】:

  • 很好的答案。感谢布赖恩的洞察力。哦,我完全同意 DDE。在我们说话的时候,我正在给供应商发电子邮件。
【解决方案2】:

变得真实 ;) DDE 很慢,图形很慢。不要在同一个线程中执行它们。

试试看:

  • 创建处理 DDE 的第二个线程,对项目进行排队。
  • 然后图表线程提取更新并绘制它们。

现在,重点来了:

  • 仅允许 ui 线程修改图表控件。是的,糟透了。不,没有商量余地。 - 自古以来的 UI 规则。
  • 线程需要锁定 ;)

【讨论】:

    猜你喜欢
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 2021-04-04
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多