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

Вниз

Работа с БД в отдельном треде.   Найти похожие ветки 

 
Alex_C   (2012-03-11 11:52) [0]

В продолжении темы о необходимости нескольких ADOConnection, если работа с базой происходит в отдельном треде.
Вот есть какая проблема.
Дано ADO+Access.
Есть ClientDataSet - база клиентов фирмы.
Есть SpravDataSet - общий справочник жителей города N.
После того, как в ClientDataSet вносится новый клиент нужно в справочной базе SpravDataSet проверить, есть ли такой человек, если есть - отметить, что он теперь клиент фирмы.
Делаем это на событие AfterPost для ClientDataSet.
Дабы не происходило зависания программы при поиске соответствующей записи в базе справочника SpravDataSet , было принято решение работу с ним вынести в отдельный тред.
В отдельном треде создаем свой ADOConnection и ADODataSet для соединения с базой справочника. Делаем необходимые корректировки в базе - тут все ок, корректировки проходят как надо.
Теперь нам нужно обновить содержание окна, которое содержит таблицу со SpravDataSet . Для этого перед завершением работы треда вызываем метод Synchronize, в котором делаем  

SpravDataSet.CommandText := "SELECT * FROM..."
SpravDataSet.Active := True;

И видим, что у нас отображаются "старые" данные по справочнику, без учета внесенных в треде изменений.
Если же затем мы вручную, по кнопке выполним код

SpravDataSet.CommandText := "SELECT * FROM..."
SpravDataSet.Active := True;

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


 
sniknik ©   (2012-03-11 12:02) [1]

кончай херней маяться... обучись хоть чему то. проверка/запрос на 1 запись делается тысячную долю секунды, куда там поток? у тебя дольше? читай пост сначала.


 
Сергей М. ©   (2012-03-11 13:51) [2]


> После того, как в ClientDataSet вносится новый клиент нужно
> в справочной базе SpravDataSet проверить, есть ли такой
> человек


Хрень какая-то ...

в ClientDataSet нет и не должно быть никакого "человека" - ни такого ни другого - там должна быть ссылка на соотв.элемент SpravDataSet.

Если на момент подтверждения создания записи в ClientDataSet (событие AfterPost) в SpravDataSet  еще нет соотв.записи, то это ситуация сознательного и грубого нарушения ссылочной целостности, чреватая граблищами.


 
Alex_C   (2012-03-11 15:12) [3]


> проверка/запрос на 1 запись делается тысячную долю секунды,
>  куда там поток? у тебя дольше?


У меня дольше. Потому как я пытался тут максимально упростить объяснение задачи.
В реальной задаче приходится проверять более сложное условие. Выполнение запроса без отдельного треда получается порядка 2 сек - что для моего условия не приемлимо по скорости.


> в ClientDataSet нет


Прошу прощения - скажем так - имя его должно быть типа SpisokClientDataSet: TADODataSet. Неудачное название для примера подобрал, не обратя внимание , что оно совпадает с элементом TClientDataSet.


 
Alex_C   (2012-03-11 15:16) [4]

Да, саму работу в отдельном треде делал согласно этой рекоммендации.
http://delphi.about.com/od/kbthread/a/query_threading.htm


 
sniknik ©   (2012-03-11 15:30) [5]

> упростить объяснение задачи.
УПРОСТИТЬ??? а где ДЕЙСТВИЯ??? что ДЕЛАЕТСЯ? описания не связанной херни, выдуманной необходимости потоков, и других ДОМЫСЛОВ, "пугалок" (о сложных условиях), и приемлемости ничем не подтвержденного (и скорее ошибочного, из-за кривых рук результата) ничем не упрощают понимание того, что делается/в чем проблема.

проверка
запрос на обновление -
UPDATE n_kladr
SET Name = "Северный"
WHERE Code = "2301500003000"


в базе 204686 записей (кладр, больше чем у тебя?), поле code индексное (не ключ), по "плохому" строковому полю, т.е. не самое быстрое, выполнение идет - ... а не знаю сколько, у меня в тесте деление до тысячных секунд, показывает - 00:00.000.
ГДЕ НУЖЕН ПОТОК?


 
Ega23 ©   (2012-03-11 15:36) [6]


> проверка
> запрос на обновление -


Я не сильно вникал, но по-моему ему нужна конструкция Insert or Update. Которая есть далеко не везде.


 
Anatoly Podgoretsky ©   (2012-03-11 15:37) [7]


> Да, саму работу в отдельном треде делал согласно этой рекоммендации.

CoUninitialize не защищен секцией try, код опасный


 
Anatoly Podgoretsky ©   (2012-03-11 15:39) [8]

Кроме того никакой конкретики, код не приведен. Разговор ни о чем.


 
Alex_C   (2012-03-11 15:41) [9]

Послушай, я хочу не в данной конкретной задаче, а для себя понять, как работать в отдельном треде с БД. Работа с БД в отдельном треде не считается чем то плохим.
Вот сейчас у меня возник конкретный вопрос: я в отдельном треде изменил данные. Теперь эти измененные данные я хочу отобразить в таблице.
По твоему негативному к этому отношения, я так понимаю ты считаешь что работать с БД в отдельном треде не следует. Ок! Я твою точку зрения понял.
Теперь вопрос к другим: это все так считают, что следует избегать рабоать с БД в отдельном треде?


 
Alex_C   (2012-03-11 15:42) [10]

[9] пост относился к sniknik.


 
sniknik ©   (2012-03-11 15:46) [11]

> проверка
"усложнил"
UPDATE n_street
SET Name = "Межевая"
WHERE Code = "47010011003000600"

на более объемной таблице - 899615 записей, индекс "шире". время тоже - 00:00.000.
НА ХРЕНА ПОТОКИ?

> согласно этой рекоммендации.
в рекомендации может быть все идеально, но главный глюк не рекомендации/справка/система/объект/компонент, и т.д., а ТЫ. именно ты делаешь глюки, именно ТВОЙ код нужно проверять, пусть он и сделан по самой расчудесной рекомендации.

p.s. как-то читал про культуристов... их рекомендации. так основной и самой первой было - "встаньте перед зеркалом и перечислите все свои недостатки. честно, не лукавя. т.к. если вы будете кивать на другое, кривое зеркало там/неправильное мнение о вас окружающих... толку не будет".
а вот у начинающих "программистов" процветает самообман... типа у меня все правильно, остальное виновато. и если честно сказать такому глюк это ТЫ, начинаются "визги", ой меня оскорбили...
но это правда.


 
Alex_C   (2012-03-11 15:46) [12]

ок! вот реальный код.


procedure TRecalcColorThread.Execute;
var
 FDataSet: TADODataSet;
 FLogDataSet: TADODataSet;
 S: string;

 {$I GetCountryIndex.pas}

begin
 CoInitialize(nil);
 SpotMachineForm.SpotResLock.Lock;
 try
   FDataSet := TADODataSet.Create(nil);
   with FDataSet do
   begin
     ConnectionString := DataBaseConnectionString;
     CursorLocation := clUseServer;

     if FCountry = "" then
       CommandText := "SELECT * FROM SpotTable"
     else
       CommandText := "SELECT * FROM SpotTable WHERE Country = " + QuotedStr(FCountry);
     Active := True;
     if RecordCount > 0 then
     begin
       FLogDataSet := TADODataSet.Create(nil);
       with FLogDataSet do
       begin
         //Connection := FLogConnection;
         ConnectionString := LogConnectionString;
         CursorLocation := clUseServer;
         S := "";
         if PaperQSLR  then
           S := "QSLR";
         if LoTWQSLR then
         begin
           if S = "" then
             S := "LoTWR"
           else
             S := S + "+LoTWR";
         end;
         if eQSLR then
         begin
           if S = "" then
             S := "eQSLR"
           else
             S := S + "+eQSLR";
         end;
         if S = "" then
           S := "0";
         if FCountry = "" then
           CommandText := "SELECT Country, BandADIF, Mode, SUM(" + S + ") AS QSL " +
             "FROM " + MainForm.LogName + " WHERE Country <> " + QuotedStr("") +
             "GROUP BY Country, BandADIF, Mode ORDER BY Country "
         else
           CommandText := "SELECT Country, BandADIF, Mode, SUM(" + S + ") AS QSL " +
             "FROM " + MainForm.LogName + " WHERE Country = " + QuotedStr(FCountry) +
             "GROUP BY Country, BandADIF, Mode ORDER BY Country ";
         Active := True;
       end;
       //**********************************************************************
       First;
       while not Eof do
       begin
         Edit;
         FieldByName("IsNewCountry").AsInteger :=
           GetCountryStatus(FieldByName("DXCall").AsString,
               FieldByName("BandADIF").AsString,
               FieldByName("Mode").AsString);
         Post;
         Next;
       end;
       FreeAndNil(FLogDataSet);
     end;
   end;
 finally
   FreeAndNil(FDataSet);
   SpotMachineForm.SpotResLock.UnLock;
   CoUninitialize;
 end;
 Synchronize(RefreshWithSpotWindow);
end;


 
RWolf ©   (2012-03-11 15:47) [13]


> это все так считают, что следует избегать рабоать с БД в
> отдельном треде?

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


 
sniknik ©   (2012-03-11 15:48) [14]

> я так понимаю ты считаешь что работать с БД в отдельном треде не следует. Ок! Я твою точку зрения понял.
опять ДОМЫСЛЫ. ламер.


 
Alex_C   (2012-03-11 15:51) [15]

Небольшое объяснение к коду:
SpotTable - это собственно та таблица, которую нежно изменять. Как видно из кода, при некоторых условиях приходится изменять все данные в этой таблице. При некоторых - только часть.
Для каждой выбранной из SpotTable записи необходимо проверить/изменить ее статус.
В итоге время получается около 2 сек.


 
RWolf ©   (2012-03-11 15:51) [16]

       First;
       while not Eof do
       begin
         Edit;
         FieldByName("IsNewCountry").AsInteger :=
           GetCountryStatus(FieldByName("DXCall").AsString,
               FieldByName("BandADIF").AsString,
               FieldByName("Mode").AsString);
         Post;
         Next;
       end;


вот этот апдейт *всех* записей таблицы, он зачем?


 
Alex_C   (2012-03-11 15:52) [17]


> ламер.

Да. Ламер. И пишу в соответствующей теме. Если тебе это так не приятно - просто пройди мимо.


 
Anatoly Podgoretsky ©   (2012-03-11 15:54) [18]

> Alex_C  (11.03.2012 15:46:12)  [12]

Почему clUseServer?


 
Alex_C   (2012-03-11 15:56) [19]


> вот этот апдейт *всех* записей таблицы, он зачем?


Не всех, а в соответствии с условием

     if FCountry = "" then
       CommandText := "SELECT * FROM SpotTable"
     else
       CommandText := "SELECT * FROM SpotTable WHERE Country = " + QuotedStr(FCountry);
     Active := True;


Все записи обходятся только при открытии БД - в остальных случаях только те, что нужны.


 
sniknik ©   (2012-03-11 15:59) [20]

> Почему clUseServer?
он думает так не будет "закачки всего", правильно в общем то, для access, но для этого нужно соблюсти все условия...
к примеру не пользоваться  RecordCount, т.к. встречая его ADO начнет закачку чисто посчитать. (сработает как fetch al)l, насколько помню)

а вот почему прямые обращения из потока типа MainForm.LogName?


 
Alex_C   (2012-03-11 15:59) [21]


> Почему clUseServer


Пробовал и clUseClient - но разницы не было.
Кстати а как в данном случае правильно?


 
Юрий Зотов ©   (2012-03-11 15:59) [22]


> Alex_C   (11.03.12 15:41) [9]
> это все так считают, что следует избегать
> рабоать с БД в отдельном треде?

От задачи зависит. Например, если данные поступают от разных юзеров, то можно для каждого юзера создать отдельный поток и этот поток будет коннектиться к БД от имени этого юзера. Юзер разлогинился - его поток завершился и уничтожился.

Если же поток создается только лишь для того, чтобы не "подвисал" GUI, то надо прикинуть, а нужно ли это реально. Если, скажем, вся операция обновления длится доли секунды, то вряд ли для нее нужен отдельный поток. Тем более, если этот поток имеет свой собственный коннект - сама операция коннекта может длиться дольше, чем все остальное.

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


 
Alex_C   (2012-03-11 16:02) [23]


> он думает так не будет "закачки всего", правильно в общем
> то, для access, но для этого нужно соблюсти все условия.
> ..
> к примеру не пользоваться  RecordCount, т.к. встречая его
> ADO начнет закачку чисто посчитать. (сработает как fetch
> al)l, насколько помню)


Совершенно верно так я и думаю.


> не пользоваться  RecordCount, т.к. встречая его ADO начнет
> закачку чисто посчитать.


Проверял по времени - вроде как даже при его использовании все равно существенно быстрее, если при большой бд делать clUseClient.


 
Юрий Зотов ©   (2012-03-11 16:02) [24]

> пока он ее не закоммитит, другие коннекты изменений не увидят

Если, конечно, не настроены соответствующим образом уровни изоляции.


 
sniknik ©   (2012-03-11 16:09) [25]

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

> Если, конечно, не настроены соответствующим образом уровни изоляции.
в ADO + Jet, уровни изоляции это отдельные COM объекты на каждое подключение (о чем я ему говорил уже). т.е. сделал отдельный... и жди пока он данные в файл скинет чтобы другой мог перечитать...
а вот с асинхронностью, когда он сам потоки делает, там все в порядке, сам делает, сам разбирается, что где, может и из буфера прочитать.


 
Alex_C   (2012-03-11 16:14) [26]


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


Совершенно верно - это я и хочу понять!


> настроены соответствующим образом уровни изоляции.


ilCursorStability установлены.


 
RWolf ©   (2012-03-11 16:21) [27]

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


 
знайка   (2012-03-11 16:21) [28]


> Для каждой выбранной из SpotTable записи необходимо проверить/изменить
> ее статус.
Одним запросом это никак?


 
sniknik ©   (2012-03-11 16:23) [29]

> ilCursorStability установлены.
пофигу. но как тест, где? код твой приведен выше, создание компонент динамическое, где присваивается ilCursorStability?


 
Ega23 ©   (2012-03-11 16:28) [30]

Зачем нужна многопоточность в БД-приложении:
1. Пишем сервер приложений. Например - бэкенд за http-сервером. Надеюсь, тут объяснять не нужно.
2. Двухзвенка. Тут вариант простой: нужно ли нам залипание гуя во время длительных операций? Нужно - внедряем. Так сойдёт - не внедряем. В каких случаях залипание нежелательно? Ну, например, формируем некий офигенный отчёт. Когда snapshot-транзакция и пошло-поехало. Или длительный поиск по Full-Text Search индексу. Больше, честно говоря, не могу придумать.

А вот сборная солянка, когда что-то в основном потоке, что-то в отдельном - это скорее какая-то фигня в архитектуре. Либо всё, либо ничего (не считая этих самых отчётов и поисков).


 
Сергей М. ©   (2012-03-11 16:28) [31]


> имя его должно быть типа SpisokClientDataSet: TADODataSet.
>  Неудачное название для примера подобрал


Да не суть важно какое у него название.

Важно что это подчиненный НД.


 
sniknik ©   (2012-03-11 16:30) [32]

> но как тест
это в общем то пример, отличия "описательной" части от реальной, и чем может помочь "кивание" на рекомендации - http://delphi.about.com/od/kbthread/a/query_threading.htm
а сколько еще скрыто в "запасниках"...

> 2. Двухзвенка. Тут вариант простой: нужно ли нам залипание гуя во время длительных операций? Нужно -
внедряем асинхронность и событийность... потоки тут тоже в последнюю очередь.


 
Anatoly Podgoretsky ©   (2012-03-11 17:00) [33]

> Alex_C  (11.03.2012 15:59:21)  [21]

Ну так никакого сервера нет и в помине.


 
Ega23 ©   (2012-03-11 17:20) [34]


> внедряем асинхронность и событийность... потоки тут тоже
> в последнюю очередь.


Ну тут спорный вопрос. Можно по-разному извратиться. Но от DB-Aware отказаться придётся с большой долей вероятности.
А многие не хотят. Хотят и штоб DBGrid-ы мастре-детальные, и штоб событийность и асинхронность.


 
Alex_C   (2012-03-11 21:36) [35]


> а сколько еще скрыто в "запасниках"...
>


Хорошо. Приведу максимально полное описание проблемы.
Есть основная база (50-200тыс записей) проведенных радиосвязей.
Из интернета скачиваем базу сейчас работающих в эфире станций. Количество записей в ней от 1 до 1000.
Естественно делаем это в отдельном треде. Там же ее парсим, для каждой станции высчитываем индекс нужности (есть ли такая страна в основной базе, есть ли такая страна на таком диапазоне, виде модуляции, подтверждена. Если подтверждена, то способ подтверждения - индекс достаточно сложный и одним SQL-запросом его получить нельзя). И в этом же треде заносим полученные данные в базу данных, которая называется база спотов. Теперь эту базу нам нужно отобразить. Думаю в данном случае необходимость отдельного треда очевидна?
Но это еще не все. При изменении основной базы, в базе спотов необходимо пересчитывать индексы нужности.
Пример: в базе спотов у нас присутствует информация о 100 работающих станциях из USA. Мы в основную базу занесли новую связь с USA. Теперь для каждой записи в базе спотов нам необходимо пересчитать индекс нужности. А если мы пакетом добавили 100 связей в основную базу? Но естественно интекс нужности пересчитывать нужно для всех записей. И при этом основная программа не должна тормозиться.
Думаю, после этого подробного объяснения мой приведенный выше код стал более понятным.


 
sniknik ©   (2012-03-11 22:06) [36]

> Думаю, после этого подробного объяснения мой приведенный выше код стал более понятным.
ценность этого пояснения для понимания = 0 целых х...рен десятых. тут же вообще НИЧЕГО кроме слов.

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

> Там же ее парсим
базы, и даже таблицы не парсят, это структурированные данные. парсят текст.  

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

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

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

> Но это еще не все.
а все... не надо больше... нафиг. "глушняк" это. не выберешься ты из ламеризма с таким подходом.


 
sniknik ©   (2012-03-11 23:00) [37]

посмотри историю
http://www.delphimaster.ru/cgi-bin/forum.pl?n=1&user=Alex_C
у тебя до сих пор одни и те же проблемы, а там тебе на разные голоса твердили "так делать нельзя", ты же при внешнем "да да я слушаю и все все учитываю",  внутренне ничего не поменял/не усвоил "мне нужно именно так, а не иначе, у меня ТЗ. логика такая... сам придумал". приехали. сейчас ты на том же уровне что тогда, уже правда немного обучен "шаманским трюкам", но знания/понимания как не было так и нет.


 
знайка   (2012-03-11 23:20) [38]

А что такое индекс нужности и зачем его в базе держать (явно)? он так сложен в расчете?


 
Inovet ©   (2012-03-11 23:29) [39]

> [38] знайка   (11.03.12 23:20)
> А что такое индекс нужности и зачем его в базе держать (явно)
> ? он так сложен в расчете?

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


 
Alex_C   (2012-03-12 09:33) [40]


> динамически расчитывается хоть каждую миниту


Раньше я его динамически расчитывал при отрисовке таблицы. Но тогда я отдельно хранил статистику по каждой сработанной стране отдельно и естественно все расчитывалось практически мгновенно.
Если сейчас расчитывать индекс нужности для каждой станции из основной базы - получается задержка при отрисовке.


> он так сложен в расчете


Я описывал выше индекс нужности:

> есть ли такая страна в основной базе, есть ли такая страна
> на таком диапазоне, виде модуляции, подтверждена. Если подтверждена,
>  то способ подтверждения - индекс достаточно сложный и одним
> SQL-запросом его получить нельзя


 
Anatoly Podgoretsky ©   (2012-03-12 09:53) [41]

Получить одну запись с нужным видом модуляции и диапазоном. Это чрезвычайно сложная и длительная задача, которая к тому же требует несколько запросов.


 
sniknik ©   (2012-03-12 09:55) [42]

> Я описывал выше индекс нужности:
твои слова несут глубокий, НУЛЕВОЙ смысл происходящего, противореча сами себе.

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

и тут же -
>> - индекс достаточно сложный и одним
>> SQL-запросом его получить нельзя
??? что за бред?


 
sniknik ©   (2012-03-12 09:58) [43]

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


 
Anatoly Podgoretsky ©   (2012-03-12 10:04) [44]

Задача придумана, чтобы не работать, на что только человек не пойдет ради данной цели.


 
RWolf ©   (2012-03-12 10:40) [45]

прозреваю, что имеется в виду не тот индекс, что в БД, а просто некий спецтермин из предметной области.


 
Alex_C   (2012-03-12 14:40) [46]

Хорошо. Согласен. Задача достаточно сложна, чтоб понять ее, не зная тем более всех исходных данных.
Ок! разделим ее на простые и понятные части.

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

Часть 1. Получение спотов с интернета и занесение их в таблицу Access, с последующих показом их пользователю.

Справочная информация:
Что такое спот: это текстовые данные, содержащие информацию о работающих сейчас в эфире станциях.
Один спот содержит:
- позывной станции
- частоту
- время занесения спота на сервер спотов
- доп. инфо.
При занесении спотов в таблицу Access программы следует избегать занесения повторных спотов. Спот считается повторным, если позывной и частота в нем совпадают.
Идеология занесения спотов такая:
- если такой спот не существует - заносим его.
- если существует - проверяем время спота. Если оно больше - делаем UPDATE доп. инфо, если оно меньше - спот старый, не заносим его в базу.

Как я делал:
- в отдельном треде получал споты (их может быть от 1 до 1000)
- парсил
- ... теперь вопрос: хотелось бы и в базу заносить споты в отдельном треде, с использованием отдельного ADOConnect. Но при этом возникают проблемы при отображении принятых спотов в основном треде. Проблема заключаются в том, что споты реально на диск в саму Access-базу заносятся через 5 сек (информация взята из интернета.) Майкрософт в принципе написал как обходить это:

 JE := (CreateOleObject("JRO.JetEngine") as JetEngine);
 try
   JE.RefreshCache(FDataBaseConnection.ConnectionObject as ADODB_TLB._Connection);
 finally
   JE := nil;
 end;

Однако по какой то причине у меня это не сработало.

Почему я хочу заносить споты в отдельном треде, а не делать это допустим в методе Synchronize треда получения данных с инета: в некоторых случаях споты идут практически беспрерывно, очень сильно замедляя работу основного треда.
Вот первая часть моей задачи.


 
Inovet ©   (2012-03-12 20:15) [47]

> [40] Alex_C   (12.03.12 09:33)
> Раньше я его динамически расчитывал при отрисовке таблицы.

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


 
знайка   (2012-03-12 20:35) [48]


> теперь вопрос: хотелось бы и в базу заносить споты в отдельном
> треде
так и заносите там же где и парсите, при чем тут синхронайзы и т.п...

И напрашивается тут вин-сервис для синхронизации с инетом, раз уж эти споты постоянно идут.


 
Inovet ©   (2012-03-12 20:44) [49]

> [46] Alex_C   (12.03.12 14:40)
> Вот первая часть моей задачи.

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


 
Alex_C   (2012-03-13 09:14) [50]


> при чем тут синхронайзы и т.п...


Меня опять не поняли :)
Я и заношу, где парсю. Но по условиям задачи я МГНОВЕННО должен отобразить занесенный в базу спот!
А это у меня не получается, т.к. при использовании отдельного ADOConnect в отдельном треде, в другом ADOConnect+ADODataSet+DBGrid увидеть внесенные данные я смогу только через 5 сек - это отражено у Майкрософта.
Вот сейчас и пытаюсь обойти это правило 5 сек.
Вроде как у майкрософта это нужно обходить как написано в [46] - но пока это у меня не получилось.


 
sniknik ©   (2012-03-13 09:31) [51]

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

удачи.

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

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


 
RWolf ©   (2012-03-13 09:39) [52]

ну так, может, использовать инструмент пошустрее?
у меня вот в аналогичной задаче используется сервер БД Firebird с его системой оповещений, работает весьма быстро, задержками можно пренебречь.


 
sniknik ©   (2012-03-13 09:44) [53]

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

или ты имеешь в виду вместо автора взять программиста? под "инструмент пошустрее". тогда 100% за.


 
Anatoly Podgoretsky ©   (2012-03-13 10:32) [54]

> sniknik  (13.03.2012 09:44:53)  [53]

Инструмент называется шустрый программист с прибамбасами


 
Alex_C   (2012-03-13 12:29) [55]


>  "выносишь в тред" делается тысячную долю секунды


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

Привожу ПОЛНЫЙ код примера.
Есть форма, на ней ADOConnection1+ADODataSet1+DBGrid1, ADOConnection2+ADODataSet2+DBGrid2, ADOConnection1+ADOCommand1.


var
 i: integer;

procedure TForm3.Button1Click(Sender: TObject);
begin
 with ADODataSet1 do
 begin
   Active := False;
   CommandText := "SELECT * FROM LogTest";
   Active := True;
 end;
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
 with ADODataSet2 do
 begin
   Active := False;
   CommandText := "SELECT * FROM LogTest";
   Active := True;
 end;
end;

procedure TForm3.Button3Click(Sender: TObject);
var
 V: Variant;
 JE: JetEngine;
begin
 ADOConnection1.BeginTrans;
 {with ADODataSet1 do
 begin
   Append;
   FieldByName("Call").AsString := "RX4HX" + IntToStr(i);
   FieldByName("DateQSO").AsDateTime := Now;
   FieldByName("BandQSO").AsString := IntToStr(i);
   Post;
 end;}

 with ADOCommand1 do
 begin
   CommandText := "INSERT INTO LogTest (Call,DateQSO,BandQSO) " +
     "VALUES(:Call,:DateQSO,:BandQSO)";
   Parameters.Items[0].DataType := ftString;
   Parameters.Items[0].Value := "RX4HX" + IntToStr(i);
   Parameters.Items[1].DataType := ftDateTime;
   Parameters.Items[1].Value := Now;
   Parameters.Items[2].DataType := ftString;
   Parameters.Items[2].Value := IntToStr(i);
   Execute;
 end;
 ADOConnection1.CommitTrans;

 {V := CreateOleObject("JRO.JetEngine");
 try
   V.RefreshCache(ADOConnection1.ConnectionObject);
 finally
   V := 0;
 end;}
 JE := (CreateOleObject("JRO.JetEngine") as JetEngine);
 try
   JE.RefreshCache(ADOConnection1.ConnectionObject as ADODB_TLB._Connection);
 finally
   JE := nil;
 end;
 Button1Click(nil);
 Button2Click(nil);
 Inc(i);
end;

procedure TForm3.Button4Click(Sender: TObject);
begin
 ADOCommand1.CommandText := "DELETE * FROM LogTest";
 ADOCommand1.Execute;
 Button1Click(nil);
 Button2Click(nil);
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
 with ADOConnection1 do
 begin
   ConnectionString := ConnectionStr;
   LoginPrompt := False;
   Mode := cmShareDenyNone;
   Attributes := [];
   Open;
 end;
 with ADOConnection2 do
 begin
   ConnectionString := ConnectionStr;
   LoginPrompt := False;
   Mode := cmShareDenyNone;
   Attributes := [];
   Open;
 end;
 i := 1;
 Button1Click(nil);
 Button2Click(nil);
end;



Нажимаем Button3. Что мы видим - в DBGrid1 - есть новая запись. В DBGrid2 - нет. Ждем 5 сек - жмем Button2 - новая запись появилась и в DBGrid2.

Мой вопрос КОНКРЕТНО по этому ПОЛНОМУ примеру - как сделать , чтоб новая запись появлялась сразу и в DBGrid2?


 
sniknik ©   (2012-03-13 12:40) [56]

ты по моему не слышишь/игнорируешь того чего тебе говорят...
COM  объект, каждый коннект. и т.д. sniknik ©   (11.03.12 16:09) [25] перечитай.

не? не доходит?

> Мой вопрос КОНКРЕТНО по этому ПОЛНОМУ примеру - как сделать , чтоб новая запись появлялась сразу и в DBGrid2?
сделать все через 1 коннект.


 
Alex_C   (2012-03-13 12:43) [57]

Все! Проблема решена!
Все делается как я написал выше только


 JE := (CreateOleObject("JRO.JetEngine") as JetEngine);
 try
   JE.RefreshCache(ADOConnection2.ConnectionObject as ADODB_TLB._Connection);
 finally
   JE := nil;
 end;


RefreshCashe делается на ЧИТАЮЩИЙ  ADOConnect, а я его на пишущей ставил!

Кстати темя эта если ее погуглить весьма широко в инете обсуждается.
Для тех, кто боится быть .... привожу полный работающий код:


const
 ConnectionStr = "Provider=Microsoft.Jet.OLEDB.4.0;Password="";User ID=Admin;"+
   "Data Source=db1.mdb;Mode=Share Deny None;Extended Properties="" ;"+
   "Locale Identifier=1049;Persist Security Info=True;Jet OLEDB:System database="" ;"+
   "Jet OLEDB:Registry Path="" ;Jet OLEDB:Database Password="" ;Jet OLEDB:Engine Type=5;"+
   "Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Global Partial Bulk Ops=2;"+
   "Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:New Database Password="" ;"+
   "Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;"+
   "Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:SFP=False;" +
   "Jet OLEDB:Don""t copy locale on compact=False;";

var
 i: integer;

procedure TForm3.Button1Click(Sender: TObject);
begin
 with ADODataSet1 do
 begin
   Active := False;
   CommandText := "SELECT * FROM LogTest";
   Active := True;
 end;
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
 with ADODataSet2 do
 begin
   Active := False;
   CommandText := "SELECT * FROM LogTest";
   Active := True;
 end;
end;

procedure TForm3.Button3Click(Sender: TObject);
var
 V: Variant;
 JE: JetEngine;
begin
 ADOConnection1.BeginTrans;
 {with ADODataSet1 do
 begin
   Append;
   FieldByName("Call").AsString := "RX4HX" + IntToStr(i);
   FieldByName("DateQSO").AsDateTime := Now;
   FieldByName("BandQSO").AsString := IntToStr(i);
   Post;
 end;}

 with ADOCommand1 do
 begin
   CommandText := "INSERT INTO LogTest (Call,DateQSO,BandQSO) " +
     "VALUES(:Call,:DateQSO,:BandQSO)";
   Parameters.Items[0].DataType := ftString;
   Parameters.Items[0].Value := "RX4HX" + IntToStr(i);
   Parameters.Items[1].DataType := ftDateTime;
   Parameters.Items[1].Value := Now;
   Parameters.Items[2].DataType := ftString;
   Parameters.Items[2].Value := IntToStr(i);
   Execute;
 end;
 ADOConnection1.CommitTrans;

 {V := CreateOleObject("JRO.JetEngine");
 try
   V.RefreshCache(ADOConnection1.ConnectionObject);
 finally
   V := 0;
 end;}
 {JE := (CreateOleObject("JRO.JetEngine") as JetEngine);
 try
   JE.RefreshCache(ADOConnection1.ConnectionObject as ADODB_TLB._Connection);
 finally
   JE := nil;
 end;}
 JE := (CreateOleObject("JRO.JetEngine") as JetEngine);
 try
   JE.RefreshCache(ADOConnection2.ConnectionObject as ADODB_TLB._Connection);
 finally
   JE := nil;
 end;
 Button1Click(nil);
 Button2Click(nil);
 Inc(i);
end;

procedure TForm3.Button4Click(Sender: TObject);
begin
 ADOCommand1.CommandText := "DELETE * FROM LogTest";
 ADOCommand1.Execute;
 Button1Click(nil);
 Button2Click(nil);
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
 with ADOConnection1 do
 begin
   ConnectionString := ConnectionStr;
   LoginPrompt := False;
   Mode := cmShareDenyNone;
   Attributes := [];
   Open;
   Properties.Item["Jet OLEDB:Transaction Commit Mode"].Value := 1;
   Properties.Item["Jet OLEDB:Shared Async Delay"].Value := 0;
   Properties.Item["Jet OLEDB:User Commit Sync"].Value := 1;
   Properties.Item["Jet OLEDB:Implicit Commit Sync"].Value := 1;
   Properties.Item["Jet OLEDB:Flush Transaction Timeout"].Value := 0;
 end;
 with ADOConnection2 do
 begin
   ConnectionString := ConnectionStr;
   LoginPrompt := False;
   Mode := cmShareDenyNone;
   Attributes := [];
   Open;
   Properties.Item["Jet OLEDB:Transaction Commit Mode"].Value := 1;
   Properties.Item["Jet OLEDB:Shared Async Delay"].Value := 0;
   Properties.Item["Jet OLEDB:User Commit Sync"].Value := 1;
   Properties.Item["Jet OLEDB:Implicit Commit Sync"].Value := 1;
   Properties.Item["Jet OLEDB:Flush Transaction Timeout"].Value := 0;
 end;
 i := 1;
 Button1Click(nil);
 Button2Click(nil);
end;

end.



 
sniknik ©   (2012-03-13 12:45) [58]

и кстати
ADOConnection1.BeginTrans; в некоторых случаях не работает, т.к. может быть локальным, а не от движка. пользуйся запросами. (одна из причин почему JRO мог не сработать, вторая в том что RefreshCache делается читающей стороной...)


 
Alex_C   (2012-03-13 12:46) [59]

Да, вот еще информация, на которую следует обратить внимание:


JetEngine object"s RefreshCache Method
There is a separate read-cache for each connection object. Calling the RefreshCache method of the JetEngine object, as exposed by the Microsoft Jet and Replication Objects type library, immediately refreshes the read-cache for the Connection objects passed as a method argument. The read-cache for all other Connection objects in the application are not affected.
Back to the top
"Jet OLEDB:Transaction Commit Mode" Connection Property
In Microsoft Jet 2.x and earlier, all writes were immediately committed. With Win32 and multi-threading, Microsoft Jet introduced a lazy-write mechanism. Setting this property to a value of 1 causes all transaction commits to be written immediately to disk for that Connection object. All other Connection objects are unaffected.

These methods are preferable to modifying registry values to get the same effect because you can precisely control where you need this value and they are more deterministic in operation. Global registry programs will adversely affect engine performance in other areas and in other applications.


 
sniknik ©   (2012-03-13 12:46) [60]

нда, опоздал.
> RefreshCache делается читающей стороной...


 
Anatoly Podgoretsky ©   (2012-03-13 12:46) [61]

> Alex_C  (13.03.2012 12:29:55)  [55]

Что же ты хочешь когда у тебя два соединения.


 
sniknik ©   (2012-03-13 12:47) [62]

> Да, вот еще информация, на которую следует обратить внимание:
тебе бы обратить внимание что проблема решилась только при переходе на конкретику, без "описательных"(бездоказательных)  "мемуаров".


 
sniknik ©   (2012-03-13 12:48) [63]

> что проблема решилась
одна из, и то придуманная из-за кривой логики.


 
Alex_C   (2012-03-13 17:25) [64]


> придуманная из-за кривой логики


Раз проблема решилась, немного не в тему, но думаю для сведения интересно:
- прикольно, но сегодня утром ко мне в аську постучал старый знакомый из другого города. Тоже радиолюбитель и тоже автор лога для радиолюбителей. Только в отличии от меня, он профессиональный программист, специализирующийся на БД (причем очень неплохой программист).
Самое смешное, что он хотел обсудить проблему занесения спотов в базу (то, что я как раз тут и выяснял).
Будучи профессиональным программистом, базу он выбрал себе FireBird.
Так вот - тут как раз с ним обсуждали вопросы логики работы этой базы. Я ему про эту тему рассказал.
Так вот он поугарал и мне сказал: если б я не знал, что ты за программу пишешь, я был бы на стороне  sniknik, но т.к. я знаю всю специфику - молодец что "добил" тему до конца :) Другого тут решения быть не может.
Теперь буду ждать выходных - будут соревнования, проверю свой лог при большом количестве приходящих спотов.


 
sniknik ©   (2012-03-13 17:51) [65]

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

реально М О Л О Д Е Ц !!!

(хотя вообще то все выше сарказм, а то еще поймешь "по своему")

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

ты сколько это "бил" чтобы "добить"? а мне пару несколько минут понадобилось. но тем не менее другого ты от меня (и других тут тоже) не воспринимаешь... типа ЭТО ВЫ НЕ ПОНИМАЕТЕ.
!!!???
да как хочешь, заблуждайся дальше.


 
Alex_C   (2012-03-14 14:32) [66]

Я вот спросить хочу: может я когда то тебя чем то обидел? Или ты просто обижен жизнью, а на мне пытаешься отыграться? Ты чего тут кричишь?
Я наверное давно должен быть обиженным и уйти. Но я на людей привык не обижаться, по этому тебе отвечу.


> для каждой таблицы отдельную базу держишь


У меня в логе всего 2 БД - в одной сам лог и относящиеся к нему справочные таблицы(диапазоны, модуляция) и вторая БД - там около 15 справочных БД.
Все как и обсуждалось давно.
Ты вот тут чего х.. несешь?


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


Я тебе еще раз говорю - там все только то, что нужно. Как ты вообще можешь судить "о вкусе фуагра", если ты его не ел?


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


Предупреждаю: адрес этой темы я дал еще нескольким проф программистам - я у них давно советы спрашиваю.
Ты реально думаешь что АБСОЛЮТНО ВСЕ можно сделать с помощью SQL-запроса? Тем, кто читает эту тему - а они так такого SQL-запроса сделать не смогли (хотя тоже говорили, что смогут - очень интеренсно). Не испугаешься ответить за свои слова - давай мне на мыло. Напишу полностью всю задачу. От меня и от моих знакомых - пару ящиков пива тебе обеспечены, если сделаешь.


> об ускорении, делаешь все наиболее тормозными способами


Ну учитывая, что быстрее, чем у меня лог ни у кого не работает... Наверное я гений! )))


> чуть ли не силком реальный код


Я спросил конкретный вопрос. Я не спрашивал - как делать. Я спросил - как сделать именно так.


> ЭТО ВЫ НЕ ПОНИМАЕТЕ


Чего кричишь? :)


> но тем не менее другого ты от меня


Помнишь ты привел мне пример про качков? Про зеркало? Помнишь?
Так вот: учитывая, что в 20 лет у меня был КМС по атлетическому троеборью скажу тебе так:
у качков самое доброжелательное отношение к тому, кто спрашивает твоего совета. Сильные люди - они добрые. :) Это слабаки пытаются хамя свое сомомнение поднять.
И тем не менее: я даю тебе шанс - миши мне на емайл - дам тебе полную катрину виденья. Поможешь - молодец. А так.... говорить все умеют.

И тем не менее:
Еще раз хочу выразить призательность многим присутствующим тут форумчанам за помощь! Никогда не забуду помощь Сергей М. в организации работе телнета - сколько лет прошло, а лучше чем в моей программе телнет ни у кого не работает))))



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

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

Наверх





Память: 0.71 MB
Время: 0.059 c
2-1336222680
Глеб
2012-05-05 16:58
2013.03.22
Редактирование надписей в компоненте едит


2-1329332051
Zheksonz
2012-02-15 22:54
2013.03.22
Быстрое сохранение параметров в файл


15-1337097562
brother
2012-05-15 19:59
2013.03.22
Интересно мнение:


1-1294760608
polyaev
2011-01-11 18:43
2013.03.22
Прошу помощи в задании


15-1352449269
tur0k_mag
2012-11-09 12:21
2013.03.22
cxTreeList + поле типа Combobox





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский