![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Firebird, Поток и DeadLock [D7, XP]
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)
> Хотел попросить помощи как мне оптимизировать код заполнения таблицы?
Может стОит оптимизировать алгоритм и выкинуть из таблицы
> Поле сколько дней до дня рождения
и сделать его вычисляемым
?