Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Многопоточность и БД 
:(
    Опции темы
OVT
Дата 19.4.2010, 12:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 19
Регистрация: 29.8.2006

Репутация: нет
Всего: нет



Добрый день!
Есть приложение (delphi 6, Windows XP SP3), предназначенное для синхронизации данных в двух различных БД (MS SQL 2005 через ADO и Sybase 7 через BDE). Обработка данных происходит по зацикленному сценарию, состоящему из последовательного вызова нескольких процедур. Принцип работы каждой процедуры заключается в чтении данных с помощью TQuery в динамические массивы из каждой БД, сравнении прочитанных данных и, в зависимости от результатов сравнения, добавлении, удалении или обновлении данных в этих БД. Весь сценарий обработки вынесен в отдельный поток. На главной форме приложения отображается ход (TProgressBar) и результаты (TMemo, TChart) обработки данных. Кроме того, главная форма содержит несколько параметров (TEdit, TCheckBox), используемых при обработке данных в потоке.
Все "общение" между рабочим потоком и потоком приложения происходит через Syncronize, т. е. ни к каким глобальным переменным или контролам главной формы рабочий поток на напрямую не обращается. Для рабочего потока в общем TDataModule созданы TDataBase и TADOConnection для подключения к соответствующим БД, а для TDataModule еще и отдельный TSession. Вроде, все рекоммендации, изложенные в статье Петровича  http://forum.vingrad.ru/topic-60076.html (огромная благодарность автору!), мною учтены, программа работает, но наблюдается постоянный рост объема используемой памяти ~ 20MB за один цикл. Таким образом, через 50 - 60 циклов программа занимает около 1.2 GB и начинаются ошибки, в основном при обращении к MS SQL, типа "истек таймаут", "неверные параметры хранимой процедуры" и т. п.
Подозревая наличие утечки памяти, пытался использовать FastMM4, но отчет показал наличие всего нескольких утечек, и то, похоже, не моих, по несколько десятков байт:
13 - 20 bytes: TObjectList x 3, Unknown x 3
21 - 36 bytes: TWinHelpViewer x 1
37 - 52 bytes: THelpManager x 1
Хотя с FastMM4 работаю впервые и, скорее всего, просто не умею им пользоваться.
Некоторое время назад я написал аналогичное приложение, в котором не использовал потоки и ADO. Подобных фокусов с пожиранием памяти в нем я не наблюдал. Думаю, что собака порылась либо в потоке (видимо я упустил что-то важное для работы с БД), либо это связано с ADO, или тем, как я его использую в отдельном потоке.
Не подскажете, где мой косяк?

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

Код

program DeltaServer;
uses
//  FastMM4 in 'FastMM4.pas',
  FixBDE4GbBug in 'Units\FixBDE4GbBug.pas',
  Forms,
  Main in 'Units\Main.pas' {fmMain},
  Settings in 'Units\Settings.pas' {fmSettings},
  DataModule in 'Units\DataModule.pas' {dmDataModule: TDataModule},
  Cycle in 'Units\Cycle.pas';

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TfmMain, fmMain);
  Application.CreateForm(TfmSettings, fmSettings);
  Application.CreateForm(TdmDataModule, dmDataModule);
  Application.Title := 'DeltaServer';
  Application.Run;
end.


Главная форма
Код

type
  TfmMain = class(TForm)
    mmMainMenu: TMainMenu;
    miStart: TMenuItem;
    miStop: TMenuItem;
    miExit: TMenuItem;
    meLog: TMemo;
    pbCycle: TProgressBar;
    procedure miExitClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure miStartClick(Sender: TObject);
    procedure WorkLogUpdate(Key : String);
    procedure FormCreate(Sender: TObject);
    ...
  private
    { Private declarations }
  public
    { Public declarations }
  end;

procedure TfmMain.miStartClick(Sender: TObject);
begin
  miStart.Enabled := False;
  miStop.Enabled := True;
  flStop := False;
  flStopping := False;
  //стартуем дочерний поток
  MainCycle := TMainCycle.Create(True);
  MainCycle.FreeOnTerminate := True;
  MainCycle.Priority := tpNormal;
  MainCycle.Resume;
end;

procedure TfmMain.miStopClick(Sender: TObject);
begin
  flStopping := True;
  MainCycle.Terminate;
end;

procedure TMainCycle.AddLog;
begin
  fmMain.meLog.Lines.Add(LogRecord);
end;

procedure TfmMain.FormShow(Sender: TObject);
begin
  if dmDataModule.ssThreadSession.Active then
    dmDataModule.ssThreadSession.Active := False;
  dmDataModule.ssThreadSession.Active := True;
  ...
end;


Поток
Код

  TMainCycle = class(TThread)
  private
    CycleProgressMin: Integer;
    CycleProgressMax: Integer;
    CycleProgressPosition: Integer;
    UserName: String;
    Password: String;
    ConnectionString: String;
    Stopping: Boolean;
    procedure AddLog;
    procedure CycleProgressInit;
    procedure CycleProgressInc;
    procedure OptionsSync;
    ...
  protected
    procedure Execute; override;
  end;

procedure TMainCycle.CycleProgressInit;
begin
  fmMain.pbCycle.Min := CycleProgressMin;
  fmMain.pbCycle.Max := CycleProgressMax;
  fmMain.pbCycle.Position := CycleProgressPosition;
end;

procedure TMainCycle.CycleProgressInc;
begin
  fmMain.pbCycle.StepIt;
end;

procedure TMainCycle.GetStopping;
begin
  Stopping := flStopping;
end;

procedure TMainCycle.Execute;
begin
  LogRecord := 'Process started on ' + DateToStr(Now) + ' at ' + TimeToStr(Now);
  Synchronize(AddLog);
  if dmDataModule.dbComTec.Connected then
    dmDataModule.dbComTec.Connected := False;
  Synchronize(GetOptionUserName);
  dmDataModule.dbComTec.Params[0] := 'USER NAME=' + UserName;
  dmDataModule.dbComTec.Params[1] := 'PASSWORD=' + Password;
  dmDataModule.dbComTec.AliasName := MidStr(Task, 1, Pos(' % ', Task)-1);
  dmDataModule.dbComTec.Connected := True;
  if dmDataModule.dbOptimum.Connected then
    dmDataModule.dbOptimum.Connected := False;
  dmDataModule.dbOptimum.ConnectionString := ConnectionString;
  dmDataModule.dbOptimum.Connected := True;
  Synchronize(GetStopping);
  while Not Stopping do begin
    CycleProgressMin := 0;
    CycleProgressMax := 12;
    CycleProgressPosition := 0;
    Synchronize(CycleProgressInit);
    // Опции системы ***********************************************************
    OptionsSync;
    Synchronize(CycleProgressInc);
    ...
    if MainCycle.Terminated then begin
      Stopping := True;
      Synchronize(SetStopping);
    end;
  end;
  if dmDataModule.dbOptimum.Connected then
    dmDataModule.dbOptimum.Connected := False;
  if dmDataModule.dbComTec.Connected then
    dmDataModule.dbComTec.Connected := False;
end;



Это сообщение отредактировал(а) OVT - 19.4.2010, 13:51
PM MAIL   Вверх
imageman
Дата 29.4.2010, 17:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 66
Регистрация: 30.9.2004

Репутация: нет
Всего: 1



я бы (для начала) открывал доступ к БД из основного потока (например при нажатии на кнопку Старт) и не закрывал бы связь с БД на протяжении всего рабочего времени... Что-то мне подсказывает, что проблема может быть в частом отрытии-закрытии. Но у меня мало опыта.

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

Это сообщение отредактировал(а) imageman - 29.4.2010, 17:17
PM MAIL   Вверх
OVT
Дата 4.5.2010, 10:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 19
Регистрация: 29.8.2006

Репутация: нет
Всего: нет



На самом деле доступ к БД открывается один раз, когда стартует поток по нажатию на кнопку "Старт". Дальнейшая обработка крутится внутри Execute до тех пор, пока в основном потоке не будет нажата кнопка "Стоп".
У меня закралось подозрение, что я не правильно настраиваю (точнее оставляю по умолчанию все свойства) TADOQuery и TADOStoredProc. При подсчете количества записей в запросе (RecordCount) здорово увеличивается объем используемой памяти, гораздо больше, чем требуется для помещения в память всего результата запроса. После подсчета числа записей я аллокирую массив под данные - память расходуется, но в сотни раз меньше. Дальше, когда я начинаю последовательно читать записи и заносить данные в массив, память опять расходуется. При закрытии запроса (Active := False) память не освобождается полностью. Складывается такое ощущение, что память, израсходованная при подсчете числа записей, не освобождается. 
По диспетчеру задач очень сложно определить количество памяти, занимаемое приложением в тот или иной момент времени, для детального анализа и поиска утечки памяти. Если я правильно понимаю, то не существует функции, возвращающей объем памяти, занятый конкретным приложением. По крайней мере, я пока не нашел такой. 
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Базы данных и репортинг"
Vit
Петрович

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами


Обязательно указание:

1. Базы данных (Paradox, Oracle и т.п.)

2. Способа доступа (ADO, BDE и т.д.)


  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • Вопросы по SQL и вопросы по базам данных не связанные с Дельфи задавать здесь

FAQ раздела лежит здесь!


Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Vit, Петрович.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Базы данных и репортинг | Следующая тема »


 




[ Время генерации скрипта: 0.0467 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.