Как кэшировать данные в реальном времени?

  • Я работаю над приложением Windows Forms (.NET 4.0).
  • Моя форма содержит диаграмму Fast Line с использованием элемента управления диаграммой Microsoft, включенного в VS2010.
  • Диаграмма заполняется примерно 20 000 точек данных.
  • Затем мое приложение начинает получать рыночные данные с сервера через DDE (динамический обмен данными) в режиме реального времени и добавляет их на график.

Примечание. Я не контролирую сервер, поэтому мне приходится иметь дело только с DDE, несмотря на то, что это устаревшая технология. VS больше не поддерживает DDE, поэтому я использую библиотеку Ndde, которая прекрасно работает.

Сначала мы подключаемся к серверу, создаем цикл Advise, а затем подписываемся на событие 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() больше не помогает)

Единственная помощь, которую я смог найти в Интернете, — это старый пост, в котором кто-то упоминал, что вы должны помещать входящие данные в очередь кеша, при этом из кэша высвобождается одно новое значение данных (каждый раз, когда диаграмма завершает работу).

Мой вопрос: как бы вы это сделали?

Другие предложения приветствуются!


person Marven    schedule 10.05.2011    source источник
comment
Вы случайно не обновляете коллекцию данных диаграммы из потока, отличного от пользовательского интерфейса?   -  person Gabe    schedule 10.05.2011
comment
Это похоже на проблему с потоками — что-то пытается изменить коллекцию во время ее перечисления.   -  person rsbarro    schedule 10.05.2011
comment
OnAdvise — это функция делегата события, верно? Попробуйте вызвать метод AddY вместо выполнения. (Или еще лучше, посмотрите на значение InvokeRequired. Предположительно, это значение истинно, и я так думаю, вы не можете безопасно вызывать его из этой функции, не вызывая его (синхронно передавая данные в другой поток)). Однако необходимо сохранить добавленные точки в структуре и добавить их все ПОСЛЕ того, как вы прекратите повторять точки данных. Затем коллекция не изменяется во время итерации, и следующий проход будет иметь обновленные данные.   -  person Derk-Jan    schedule 10.05.2011
comment
@Gabe & rsbarro: Да, похоже. @Derk-Jan Karrenbeld: Спасибо за ваши предложения, я изучаю их.   -  person Marven    schedule 11.05.2011


Ответы (2)


Скорее всего, это проблема, вызванная попыткой изменить элемент пользовательского интерфейса из потока, отличного от потока пользовательского интерфейса.

То, как вы это закодировали, теперь обработчик событий DdeClient.Advise выполняется в рабочем потоке, управляемом библиотекой. Видите ли, DDE отстой, и поскольку он отстой, к нему предъявляются следующие требования: он должен работать в потоке с насосом сообщений.1 Чтобы сделать библиотеку совместимой с другими типами приложений, помимо оконных форм, я закодировал ее в таким образом, что он создаст выделенный поток с циклом сообщений и маршалирует все операции в этот поток по умолчанию.

Но вы можете переопределить это поведение, указав экземпляр ISynchronizeInvoke вручную в конструкторе DdeClient. Затем библиотека будет использовать любой поток, в котором размещается экземпляр ISynchronizeInvoke, для всех своих операций DDE. Все экземпляры Form и Control реализуют ISynchronizeInvoke, поэтому достаточно просто указать библиотеке использовать основной поток пользовательского интерфейса.

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

Если вы скажете библиотеке использовать ваш экземпляр Form, то обработчики событий Advise будут выполняться в том же потоке, что и Form; поток пользовательского интерфейса.

Кстати, я понимаю, что у вас нет контроля над сервером, но я бы по крайней мере начал переговоры с производителем программного обеспечения, чтобы использовать более современные (не 20-летние) механизмы для осуществления межпроцессных взаимодействий.


1Кроме того, у него есть досадное требование сходства потоков, которое делает работу со сборщиком мусора настоящей головной болью.

person Brian Gideon    schedule 10.05.2011
comment
Отличный ответ. Спасибо за понимание Брайан. О, и я не мог не согласиться с DDE. Я пишу продавцу по электронной почте, пока мы говорим. - person Marven; 11.05.2011

Получите реальный ;) DDE медленный, графика медленная. Не делайте их в одной теме.

Попробуй это:

  • Создайте второй поток, который обрабатывает DDE, ставит элементы в очередь.
  • Затем поток диаграммы извлекает обновления и рисует их.

А теперь самое главное:

  • ТОЛЬКО поток пользовательского интерфейса может изменять элемент управления диаграммой. Да, отстой. Нет, не подлежит обсуждению. - старое правило пользовательского интерфейса с незапамятных времен.
  • Темы требуют блокировки ;)
person TomTom    schedule 10.05.2011