2
  • 我正在开发一个 Windows 窗体应用程序(.NET 4.0)。
  • 我的表单包含使用 VS2010 中包含的 Microsoft 图表控件的“快速线”图表。
  • 该图表填充了大约 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() 中的值并没有帮助)

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

我的问题是你会怎么做?

欢迎其他建议!

4

2 回答 2

2

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

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

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

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

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

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


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

于 2011-05-10T18:59:46.103 回答
0

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

试试看:

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

现在,重点来了:

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