Подтвердить что ты не робот

Каков предпочтительный способ кода сервера, уведомляющий несколько клиентов об изменениях данных в одном приложении Delphi?

У меня есть большое приложение Delphi, у которого есть основной "серверный" код, содержащий мои данные. В одном и том же приложении "клиент" пользователь может открывать и закрывать несколько немодальных "клиентских" форм для проверки этих данных. Изменения данных делятся на два типа: основные (например, структурные изменения, такие как данные были добавлены или удалены) и незначительные, такие как изменение значения данных. Существующие открытые клиентские формы должны обновляться, чтобы отображать измененные данные в течение короткого времени. Это не база данных, мой "сервер", использующий мои собственные структуры данных, поэтому мои решения, возможно, упустили, возможно, стандартные методы, доступные в официальной структуре базы данных. Тем не менее, я повторил свои решения так много раз, что думал, что я спрошу, есть ли формальные методы и, возможно, компоненты Delphi, которые улучшат или упростит мой код. Я собираюсь перейти к многопоточному коду, который делает вопрос еще более актуальным для меня.

Я использую два метода:

  • Отметка. Код "сервер" поддерживает значение Int64, взятое из QueryPerformanceCounter. Клиентские формы проверяют это значение на таймере тикания 300 мс и обновляют себя, если их копия метки времени отличается от сервера. Я предполагаю, что это мое решение "pull".

  • Уведомление об интерфейсе. Код "server" поддерживает класс, сгенерированный из TInterfaceList с помощью методов AddClient и RemoveClient, которые регистрируют простой общий клиентский интерфейс идентификации. Каждый из клиентов регистрируется с этим списком при создании и отбрасывает на уничтожение. Изменения данных на сервере запускают итерацию через этот список, вызывая каждого клиента, чтобы сообщить ему об изменении. Я думаю, это мое "push" решение.

Мне нравятся интерфейсы и решение 2, кажется приятным, так как он избегает тикающих таймеров и легко отлаживается (хотя нерегистрирующие вызовы могут быть проблематичными с порядком уничтожения). Также есть потенциальные последствия для производительности, потому что вполне вероятно, что в секунду могут произойти тысячи изменений данных, и я должен быть осторожен, чтобы использовать механизм BeginUpdate/EndUpdate для преобразования моих многочисленных изменений данных сервера в один фактический вызов. В конечном итоге мне нужен таймер какого-то типа, чтобы агрегировать вызовы в одно нежное обновление отображаемой формы.

Оба решения работают хорошо, и я разрывается между ними. Для решения mulithreaded я уверен, что есть другие подводные камни, о которых я ничего не знаю. Приветствуются любые комментарии. Я использую XE2.

4b9b3361

Ответ 1

Вам нужно принять во внимание то, что вы хотите, когда число клиентов растет, а затем решить между двумя зол:

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

Я не поклонник опроса, поскольку он обычно приводит к очень запутанным решениям (ну, по крайней мере, то, что я пробовал, возможно, я сделал это неправильно тогда).

Я бы использовал шаблон наблюдателя в Delphi с использованием интерфейсов, вы могли бы использовать this или этот в начале.

Ответ 2

A использовал Windows API для решения проблемы, аналогичной этой. Но я считаю, что мой подход проще. В моем приложении я событие не знало количество "клиентов" и которое формирует на самом деле клиентов.

Что я сделал:

  • Передача сообщения Windows во все формы, открытые моим приложением (screen.forms[X]). Сообщение включает в WParam указатель к записи, содержащей информацию о событии. Также, различные действия транслируют сообщения distinc. WM_RECORDUPDATE для обновлений БД, например.

  • Windows (клиенты) прослушивают сообщение в виде:

    procedure RecordUpdateMessage(var msg: TMessage); message WM_RECORDUPDATE;

Процедура RecordUpdateMessage читает запись, указанную в msg.WParam, и на основе данных в записи, если требуется отреагировать на обновление, вставить или удалить запись в БД.