DB-соединение в отдельном потоке - как лучше? - PullRequest
4 голосов
/ 19 февраля 2010

Я создаю приложение, которое обращается к базе данных. При каждом доступе к базе данных приложение ожидает завершения задания. Чтобы пользовательский интерфейс реагировал, я хочу поместить все элементы базы данных в отдельный поток.
Вот моя идея:

  • db-поток создает все компоненты базы данных, в которых он нуждается при создании
  • Теперь поток просто сидит и ждет команды
  • Если он получает команду, он выполняет действие и возвращается в режим ожидания. В течение этого времени основной поток ожидает.
  • дб-нить живет до тех пор, пока приложение работает

Звучит нормально?
Каков наилучший способ получить результаты базы данных из db-потока в основной поток?
Я пока мало что сделал с потоками, поэтому мне интересно, может ли db-thread создать компонент запроса, из которого основной поток считывает результаты. Основной поток и поток БД никогда не получат доступ к запросу одновременно. Это все еще вызовет проблемы?

Ответы [ 4 ]

6 голосов
/ 19 февраля 2010

Что вам нужно, так это стандартная техника доступа к данным, называемая асинхронное выполнение запроса .Некоторые компоненты доступа к данным реализуют эту функцию простым в использовании способом.По крайней мере, dbGo (ADO) и AnyDAC реализуют это.Давайте рассмотрим dbGo.

Идея проста - вы вызываете удобные методы набора данных, такие как Open.Метод запускает требуемую задачу в фоновом потоке и сразу же возвращается.Когда задача будет выполнена, будет запущено соответствующее событие, уведомляющее приложение о том, что задача завершена.

Стандартный подход с приложениями GUI БД и методом Open следующий (черновик):

  • включает eoAsyncExecute, eoAsyncFetch, eoAsyncFetchNonBlock в набор данных ExecuteOptions;
  • отключает TDataSource.DataSet от набора данных;
  • устанавливает набор данных OnFetchComplete в;
  • show «Здравствуйте! Мы выполняем тяжелую работу по обработке ваших запросов. Пожалуйста, подождите ...»;
  • вызовите метод Open набора данных;
  • при выполнении запросабудет завершено, будет вызван OnFetchComplete, поэтому P. и P скрывают диалоговое окно «Ожидание» и подключают TDataSource.DataSet обратно к набору данных.

Также ваш диалог «Ожидание» может иметькнопка Отмена, которую пользователь может использовать для отмены слишком длинного запущенного запроса.

5 голосов
/ 19 февраля 2010

Прежде всего - если у вас мало опыта работы с многопоточностью, не начинайте с классов VCL.Используйте OmniThreadLibrary по следующим причинам:

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

DB-поток создает все компоненты базы данных, которые ему нужны при создании

Это может быть не лучшим способом,Обычно я создавал компоненты только при необходимости, но не уничтожал сразу.Вы должны обязательно держать соединение открытым в потоке пула потоков и закрывать его только после того, как поток неактивен в течение некоторого времени и пул удаляет его.Но также часто рекомендуется хранить кэш объектов транзакций и операторов.

Если он получает команду, он выполняет действие и возвращается в режим ожидания.В течение этого времени основной поток ожидает.

Первая часть обрабатывается нормально, когда используется OTL.Однако - не ждите основного потока, это даст небольшое преимущество по сравнению с выполнением доступа к базе данных непосредственно в потоке VCL.Вам нужен асинхронный дизайн, чтобы наилучшим образом использовать несколько потоков.Рассмотрим стандартную форму браузера базы данных, которая имеет элементы управления для фильтрации записей.Я справляюсь с этим путем (повторного) запуска таймера каждый раз, когда меняется один из элементов управления.Как только пользователь заканчивает редактирование, запускается событие таймера (скажем, через 500 мс), и запускается задача, которая выполняет инструкцию, которая выбирает данные в соответствии с критериями фильтрации.Содержимое сетки очищается и заполняется только после завершения задачи.Это может занять некоторое время, поэтому поток VCL не ожидает завершения задачи.Вместо этого пользователь может даже изменить критерии фильтра еще раз, и в этом случае текущая задача отменяется и запускается новая.OTL дает вам событие для завершения задачи, поэтому асинхронный проект легко реализовать.

Каков наилучший способ получить результаты базы данных из db-потока в основной поток?

Обычно я не использую компоненты с поддержкой данных для многопоточных приложений БД, но использую стандартные элементы управления, которые являются представлениями для бизнес-объектов.В задачах базы данных я создаю эти объекты, помещаю их в списки, и событие завершения задачи передает список в поток VCL.

Основной поток и поток БД никогда не получат доступ к запросу одновременно.

Со всеми компонентами, которые загружают данные по запросу, вы не можете быть в этом уверены.Часто только первые записи извлекаются из базы данных, и извлечение продолжается после того, как они были использованы.Такие компоненты, очевидно, не должны совместно использоваться потоками.

3 голосов
/ 19 февраля 2010

Я реализовал обе стратегии: создание пула потоков и создание специальных потоков.

Предлагаю начать с создания adhoc-потока, его проще реализовать и масштабировать.

Перемещение в пул потоков возможно только в том случае, если (с тщательной оценкой) (1) много ресурсов (и времени) потрачено на создание потока и (2) у вас много запросов на создание.

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

Обратитесь к документации по классам, компонентам и функциям, используемым потоком, чтобы убедиться, что они потокобезопасны, то есть их можно использовать одновременно из разных потоков. Если нет, вам нужно будет синхронизировать доступ. В некоторых случаях вы можете найти небольшие различия в безопасности нитей. В качестве примера см. DateTimeToStr.

1 голос
/ 19 февраля 2010

Если вы создаете свой поток при запуске и повторно используете его позже, когда вам это нужно, вы должны обязательно отключать компоненты db (grid ..) от базового источника данных (disableControls) каждый раз, когда вы «обрабатываете»data.

Ради простоты я бы унаследовал TThread и реализовал всю бизнес-логику в своем собственном классе.Результирующий набор данных будет членом этого класса, и я бы подключил его к db-компонентам с синхронизацией.

В любом случае, также очень важно делегировать как можно больше работы серверу БД и сохранятьИнтерфейс максимально легкий.Firebird - мой любимый сервер БД: триггеры для избранных, пользовательские библиотеки UDF, разработанные в Delphi, много поточно-безопасных компонентов БД с множеством примеров и хорошей поддержкой (форум): jvUIB ...

Удачи

...