Как правильно получить доступ к результатам запроса, созданным в фоновом потоке? - PullRequest
7 голосов
/ 21 февраля 2010

Я хочу выполнить запрос к базе данных в фоновом потоке. Библиотека OmniThread поможет мне со всем, что касается потоков, но есть одна вещь, которую я до сих пор не понимаю:

Каждый поток требует отдельного подключения к базе данных. Следовательно, фоновый поток создает соединение с БД, создает запрос и затем выполняет его.

Теперь я могу получить доступ к результатам запроса, используя объект запроса фонового потока.
Но после выполнения запроса я хочу получить доступ к результатам запроса в потоке main .

Если я просто ссылаюсь на объект запроса фонового потока, это вызывает проблемы, потому что я обращаюсь к соединению БД в другом потоке?

Как я понимаю, в этом случае основной поток не будет иметь отдельное соединение с БД и будет использовать соединение из фонового потока, что не годится.

Где искажено мое мышление и как правильно это сделать?

Ответы [ 2 ]

12 голосов
/ 21 февраля 2010

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

// create and open query to fetch list of companies
while not qryCompanies.Eof do begin
  C := TCompany.Create;
  try
    C.LoadFromDataset(qryCompanies);
    Companies.Add(C);
  except
    C.Free;
    raise;
  end;
  qryCompanies.Next;
end;

C - ваш бизнес-объект для компании. Это может быть объект (TCompany) или интерфейс (ICompany), реализованный объектом. Companies - это TList<TCompany> или TList<ICompany>. В конце задания вы отправляете список компаний в ветку VCL:

Task.Comm.Send(TOmniMessage.Create(MSGID_LIST_OF_COMPANIES, Companies));

В форме, в которой вы хотите отобразить список компаний, с которыми вы обрабатываете событие OnTaskMessage экземпляра otlEventMonitor, который отслеживает вашу задачу:

procedure TListBaseFrame.otlEventMonitorTaskMessage(
  const task: IOmniTaskControl);
var
  MsgID: word;
  MsgValue: TOmniValue;
begin
  task.Comm.Receive(MsgID, MsgValue);
  Assert(MsgValue.IsInterface);
  if fLoaderTask = task then begin
    SetLoadedData(MsgID, MsgValue.AsInterface); // or MsgValue.AsObject);
    fLoaderTask := nil;
  end;
end;

Список компаний заменяет предыдущий список и может отображаться в сетке.

Аналогично, вы можете вернуть один объект / интерфейс компании для отображения и редактирования.

Две вещи, о которых стоит подумать:

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

    select * from foo where (...) order by (...)
    

    Я только когда-либо выполняю

    select id, change_index from foo where (...) order by (...)
    

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

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

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

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

Лучше всего, вероятно, не использовать компоненты с поддержкой db в графическом интерфейсе. Потоки должны взаимодействовать с базой данных и хранить информацию в бизнес-объектах, которая затем может быть отправлена ​​в основной поток (который будет их отображать).

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

...