Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2011.11.27;
Скачать: [xml.tar.bz2];

Вниз

Firebird, Поток и DeadLock   Найти похожие ветки 

 
ixen   (2011-08-04 16:11) [0]

Всем привет! Вобщем есть база на 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 из основной программы во время работы потока?

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


 
Sergey13 ©   (2011-08-04 16:20) [1]

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

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


 
DiamondShark ©   (2011-08-04 16:45) [2]


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

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


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

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


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

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


 
Loginov Dmitry ©   (2011-08-04 21:39) [3]


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


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


 
ixen   (2011-08-05 05:42) [4]


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


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


 
ixen   (2011-08-05 05:48) [5]


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

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


 
ixen   (2011-08-05 05:53) [6]


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


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


 
sniknik ©   (2011-08-05 06:58) [7]

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


 
sniknik ©   (2011-08-05 07:11) [8]

> Как это обойти и сделать более корректно.
убрать все локальные вычисления с запросами на чтение и сделать 1 запрос на массовое обновление, "расчеты" прям в нем, поток тоже убрать, т.к. время выполнения будет мизерное.

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

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


 
sniknik ©   (2011-08-05 07:20) [9]

> А это основное тело потока:
> birth_fill; // основная процедура потока

> procedure Tbirth_check.birth_fill;
> showmessage("конец заполнения");
???


 
Sergey13 ©   (2011-08-05 10:09) [10]

> [4] ixen   (05.08.11 05:42)
> но оно полезно если данные для вычисляемого поля меняются в течении работы программы

Это ты откуда подчепнул? Это, мягко говоря, не верно в принципе. Кроме того, если ты перейдешь работать в ночную смену, то после полуночи начнешь получать неверные данные. 8-)


 
DiamondShark ©   (2011-08-05 13:26) [11]

Такая маниакальная упёртость редко встречается.


 
ixen   (2011-08-05 16:24) [12]


> Sergey13 ©   (05.08.11 10:09) [10]


> если ты перейдешь работать в ночную смену, то после полуночи
> начнешь получать неверные данные. 8-)

А это уже другой вопрос... тут можно отследить смену дня и произвести расчет еще раз...


> sniknik ©   (05.08.11 06:58) [7]
>
> операция "расчета" разность это пару тактов компа с данными
> в памяти, операция записи в таблицу это миллионы тактов
> и данные на диске. боюсь, что если ты не открываешь эту таблицу пару тройку
> миллионов раз в день то оптимальнее все таки вычисляемое поле.

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


> sniknik ©   (05.08.11 07:20) [9]
>
> > А это основное тело потока:
> > birth_fill; // основная процедура потока
>
> > procedure Tbirth_check.birth_fill;
> > showmessage("конец заполнения");
> ???

showmessage стоит на время разработки, чтобы отследить завершение работы потока...

ну раз оптимальнее сделать вычисляемое поле, то буду делать вычисляемое... спасибо всем за советы..


 
ixen   (2011-08-05 16:26) [13]


> DiamondShark ©   (05.08.11 13:26) [11]
>
> Такая маниакальная упёртость редко встречается.

в споре рождается истина...


 
>|<   (2011-08-05 19:31) [14]


> в споре рождается истина...

в споре разжижается истина


 
ixen   (2011-08-08 12:45) [15]

Извеняюсь, но можно я подниму эту тему снова?

Как мне и советовали удалил поток и сделал вычисляемое поле описав его в дата сете... но это не много не удобно, так как физически его в базе нет и я не могу обращаться к его значению при помощи SQL запросов... можно ли сделать вычисляемое поле описав его в самой базе через IBExpert? В инете поискал, в IBExpert пошарился так и не понял как это сделать...


 
sniknik ©   (2011-08-08 12:52) [16]

> как это сделать...
пример из MSSQL, может и пойдет, или что то аналогичное, точнее нужно спрашивать справку по Firebird
CREATE TABLE mytable
  (
   low int,
   high int,
   myavg AS (low + high)/2
  )


 
ixen   (2011-08-08 13:08) [17]

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

Пробую выполнить вот такой скрипт:
alter table clients
  alter calc computed by (datediff(day, current_date,  cast(extract(day from birth_date)||"."||extract(month from birth_date)||"."||extract(year from current_date) as date)));


пишет:
This operation is not defined for system tables.
unsuccessful metadata update.
Cannot add or remove COMPUTED from column CALC.



 
ixen   (2011-08-08 13:16) [18]

прочитал справку :

ALTER mechanism for computed columns

Adriano dos Santos Fernandes
Tracker reference CORE-1454.
A column defined as COMPUTED BY <expression> can now be altered using the ALTER TABLE...ALTER COLUMN syntax. This feature can be used only to change the <expression> element of the column definition to a different expression. It cannot convert a computed column to non-computed or vice versa.
Syntax pattern
alter table <table-name>
  alter <computed-column-name>
  [type <data-type>]
  COMPUTED BY (<expression>);
Examples
create table test (
  n integer,
  dn computed by (n * 2)
);
commit;
alter table test
  alter dn computed by (n + n);


т.е. сказано что нельзя конверитровать вычисляемое поле в не вычисляемое и наоборот... и как быть тогда?


 
sniknik ©   (2011-08-08 13:21) [19]

> и как быть тогда?
удалить и добавить?


 
ixen   (2011-08-08 13:38) [20]

так и сделал... все получилось... но это конечно я в ручную сделал.. а мне нужно это все сделать автоматически при обновлении базы... т.е. чтоб клиент запустив программу новой версии проверил что базу тоже нужно обновить и произвел:
1) сохранить данные таблицы clients
2) удалить таблицу clients
3) создать новую с вычисляемым полем
4) залить данные обратно.... причем чтобы значение первичного ключа оставались такими же...

что лучше использовать для промежуточного хранения данных ? массив?


 
sniknik ©   (2011-08-08 14:14) [21]

> 2) удалить таблицу clients
> 3) создать новую с вычисляемым полем
1 удалить поле.
2 добавить поле.

зачем сложности? у тебя же ограничение только на конвертацию...
> нельзя конверитровать вычисляемое поле в не вычисляемое и наоборот...
или врешь?


 
sniknik ©   (2011-08-08 14:18) [22]

> хм.. т.е. вычисляемое поле можно сделать только при создании таблицы
а, понятно.
ну вот с чего ты это решил, что "только"? пример он показывает вариант, а не говорит о единственно возможном... (если дополнительно не оговорено)


 
ixen   (2011-08-08 14:25) [23]

перевел справку

A column defined as COMPUTED BY <expression> can now be altered using the ALTER TABLE...ALTER COLUMN syntax. This feature can be used only to change the <expression> element of the column definition to a different expression. It cannot convert a computed column to non-computed or vice versa.


 
ixen   (2011-08-08 14:33) [24]

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


 
sniknik ©   (2011-08-08 14:34) [25]

ну так тут написано, что теперь допустимо менять синтаксис вычисляемого поля, и не допускает смены типа поля на вычисляемое.
про запрет на добавление ничего нет. (может он и есть, как суслик, но тут этого нет)


 
sniknik ©   (2011-08-08 14:36) [26]

ну..., хорошо, что хорошо кончается.



Страницы: 1 вся ветка

Форум: "Начинающим";
Текущий архив: 2011.11.27;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.004 c
15-1311711837
tesseract
2011-07-27 00:23
2011.11.27
Чтиво на отпуск.


1-1274176852
Infarkt
2010-05-18 14:00
2011.11.27
Глюк в ValueListEditor?


15-1312191507
Dennis I. Komarov
2011-08-01 13:38
2011.11.27
Воздушные шарики


2-1312203505
Guest1
2011-08-01 16:58
2011.11.27
drag and drop из webbrowser в listbox


15-1312316995
Юрий
2011-08-03 00:29
2011.11.27
С днем рождения ! 3 августа 2011 среда





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский