Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Firebird, Поток и DeadLock [D7, XP]


Страницы:   1 2

ixen ©   (2011-08-04 16:11:00) 

Всем привет! Вобщем есть база на Firebird. В ней есть таблица клиентов (clients). В таблице есть поле дата рождения клиента и Поле сколько дней до дня рождения. Так как текущая дата с каждым днем меняется, необходимо количество дней до дня рождения обновлять при запуске программы и чтобы для пользователя сей процесс обновления был не заметен делаю его в отдельном потоке. все нормально. Программа работает и для пользователя не заметен процесс обновления, но если во время обновления таблицы clients потоком обратится к таблице clients из основной программы, то возникает DeadLock. Как это обойти и сделать более корректно.
Привожу код:
Это описание потока.

Tbirth_check = class(TThread)
 private
   cli_count, days_count:Integer;
   dbn, dbp:string;
   showmess:boolean;
   procedure showclients;
   procedure readsettings;
   procedure GetDBParams;
   procedure birth_fill;
   procedure refreshClients;
   { Private declarations }
 protected
   constructor Create;
   procedure Execute; override;
 end;


Данная процедура собственно заполняет поле birh_flag количеством дней до дня рождения относительно текущей даты. В ней я обращаюсь к каждой записи в цикле и вычисляю количество дней и записываю результат в поле birth_flag. Подключение я создаю отдельно, независимо от подключения основной программы. Из основного подключения беру только параметры подключения.


procedure Tbirth_check.birth_fill;    
var id:string;
   qr:TFIBQuery;
   db:TFIBdatabase;
   tr:TFIBTransaction;
   birth, birth_encoded:TDatetime;
   count_days,birth_day,birth_month, birth_year:word;
begin
   db:=TFIBDatabase.Create(nil);
   tr:=TFIBTransaction.Create(nil);
   db.SQLDialect:=3;
   db.DefaultTransaction:=tr;
   db.DBName:=dbn;
   db.DBParams.Text:=dbp;

   db.Open(true);
   tr.Active:=true;
   db.Execute('update clients set birth_flag=null');
   qr:=TFIBQuery.Create(nil);
   qr.Database:=db;
   qr.Transaction:=tr;
   qr.SQL.Text:='select * from clients where birth_date is not null';
   qr.ExecQuery;
   while not qr.Eof do
   begin
    birth:=qr.FldByName['birth_date'].AsDate;
    birth_year:=yearof(date);
    birth_month:=monthof(birth);
    birth_day:=dayof(birth);
    birth_encoded:=encodedate(birth_year, birth_month, birth_day);
    if birth_encoded>=date then
    begin
      count_days:=DaysBetween(Date,birth_encoded);
      id:=qr.FldByName['id'].AsString;
      db.Execute('update clients set birth_flag='+inttostr(count_days)+' where id='+id);
    end;
    qr.Next;
   end;
   qr.Free;
   cli_count:=integer(db.QueryValue('select count(id) from clients where birth_flag<='+inttostr(days_count),0));
   showmessage('конец заполнения');
   db.Close;
   tr.Free;
   db.Free;
end;


А это основное тело потока:

procedure Tbirth_check.Execute;
begin
  Synchronize(readsettings); //читаю настройки из файла и записываю переменную showmess
  Synchronize(GetDBParams); // читаю параметры основного подключения в переменные dbn и dbp
  birth_fill; // основная процедура потока
  Synchronize(RefreshClients); // Обновляю таблицу clients основной программы после заполнения
  if showmess then Synchronize(showclients); // если в настройках стоит уведомлять о днях рождения то показываю окно основной программы с клиентами у кого денюха..
end;

Хотел попросить помощи как мне оптимизировать код заполнения таблицы? Возможно ли это сделать одним запросом? Как избежать DeadLock при открытии таблицы clients из основной программы во время работы потока?

Спасибо. Надеюсь понятно описал ситуацию.


ixen   (2011-08-05 05:53:00) [21]


> Loginov Dmitry ©   (04.08.11 21:39) [3]
..........
>  А поскольку код потока написан криво, транзакция не коммитится.
>  


Я пользуюсь фибами. Процедура выполнения запроса Execute автоматически коммитит выполняемые запросы. Отдельно вызывать CommitRetaining нет необходимости.


ixen   (2011-08-05 05:48:00) [22]


> DiamondShark ©   (04.08.11 16:45) [2]
.....
> Неправда. Не возникает DeadLock. Возникает просто ожидание
> завершения транзакции.

Ну тогда exception мне врет, в котором ясно написано DeadLock. А происходит это вот почему... Во время работы потока происходит запись по очереди от строки к строке. В это время из основной программы мы пытаемся считать эти строки и отобразить их в гриде. Так как запись происходит медленнее чем считывание, процесс считывания догоняет процесс записи на какой то строке и происходит одновременное считывание и запись...в это время и происходит DeadLock.


ixen   (2011-08-05 05:42:00) [23]


> Sergey13 ©   (04.08.11 16:20) [1]
> > [0] ixen   (04.08.11 16:11)
> > Хотел попросить помощи как мне оптимизировать код заполнения
> таблицы?
>
> Может стОит оптимизировать алгоритм и выкинуть из таблицы
> > Поле сколько дней до дня рождения
> и сделать его вычисляемым
> ?
>
>


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


Loginov Dmitry   (2011-08-04 21:39:00) [24]


> Программа работает и для пользователя не заметен процесс
> обновления, но если во время обновления таблицы clients
> потоком обратится к таблице clients из основной программы,
>  то возникает DeadLock.


Firebird - версионник, посему никаких дедлоков в данном случае быть не должно. Скорее всего пытаешься из основной программы изменить запись, а эта запись уже изменена потоком. Вот программа и ждет, когда поток завершит свою транзакцию. А поскольку код потока написан криво, транзакция не коммитится. Рекомендую ознакомится с посвященными FB материалами, представленными на сайте ibase.ru. В них исчерпывающим образом дана информация о том, как надо, и как не следует работать с FB.


DiamondShark   (2011-08-04 16:45:00) [25]


> Так как текущая дата с каждым днем меняется [спасибо, кэп], необходимо
> количество дней до дня рождения обновлять при запуске программы
> и чтобы для пользователя сей процесс обновления был не заметен
> делаю его в отдельном потоке. все нормально.

Нет, не всё нормально. Это жуткая шиза. Количество дней до дня рождения вычисляется простым вычитанием двух дат (или системной функцией, или как оно там в интербейзе) и возвращается в запросе.


> если во время обновления таблицы clients потоком обратится
> к таблице clients из основной программы, то возникает DeadLock

Неправда. Не возникает DeadLock. Возникает просто ожидание завершения транзакции.


> Как избежать DeadLock при открытии таблицы clients из основной
> программы во время работы потока?

Выкинуть нахрен поток и сделать вычисляемое поле.


Sergey13   (2011-08-04 16:20:00) [26]

> [0] ixen   (04.08.11 16:11)
> Хотел попросить помощи как мне оптимизировать код заполнения таблицы?

Может стОит оптимизировать алгоритм и выкинуть из таблицы
> Поле сколько дней до дня рождения
и сделать его вычисляемым
?


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо