Форум: "Базы";
Текущий архив: 2003.06.02;
Скачать: [xml.tar.bz2];
ВнизClientDataSet -> получение данных в потоке... Найти похожие ветки
← →
BorisUK (2003-05-08 09:18) [0]Уважаемые...
Никак не могу понять как сделать такую весч как subj
При использовании обычных DataBase и Query - например все работает... а как только подставляю SoсketConnection и ClientDataSet то поулучение данных происходит в потоке, но чтото некоректно с его завершением... тоесть освобождением всех занятых ресурсов после того как данные полученны... При повторном использовании этого ClientDataSet (вообше при попытке обратится к его свойствам) сделать Close и Open Происходит зависание при первом же обращении... Ошика "Expected return values not received"
Естественно я отдаю себе отчет в некоторой разнице между доступом к данным посредством Query и посредством ClientDataSet в трехзвенке через интерфейс провайдера... Но вот как поступить в данном случае не погу придумать....
Посоветуйте пож что предпринять? или где про это почитать...? Ищу уже долго... нашел несколько примеров для Query (так у меня уже работало) а вот именно по своей проблеме ничего не нашел :(
← →
Romkin (2003-05-08 10:50) [1]Никаких проблем не должно быть. Только замечание: Все компоненты, с которыми работает поток (и TSocketConnection и TClientDataSet), должны быть созданы в контексте данного потока (в методе execute TThread например)
← →
BorisUK (2003-05-08 12:50) [2]Именно так все и происходит...
И на с датабазе и квери все работает, а тут....
В общем получаем данные в потоке в это время кажем прогресс...
Но толи чтото не так ... при повторном обращении к свойствам TClientDataSet - после того как был запущен поток... Получаем ошибку что еще не все ожидаемые данные были получены...
"Expected return values not received"
TMyThread = class(TThread)
private
cds: TClientDataSet;
ds: TDataSource;
SC: TSocketConnection;
Form : TComponent;
MyTimer : TTimer;
protected
i : Integer;
procedure DoWork;
procedure Execute; override;
procedure EndWork;
procedure onMyTimer(Sender: TObject);
public
constructor Create(aSC : TSocketConnection; aCDS : TClientDataSet; aSQL: String; aDS: TDataSource; aForm: String);
destructor Destroy;override;
end;
...
procedure TMyThread.onMyTimer(Sender: TObject);
begin
Application.ProcessMessages;
if i=1000 then i:=1;
TProgressBar(Form.FindComponent("Progress")).Position:=i;
inc(i);
end;
constructor TMyThread.Create(aSC : TSocketConnection; aCDS : TClientDataSet;aSQL: String; aDS: TDataSource; aForm: String);
begin
inherited Create(True);
Priority:=tpLowest;
Application.ProcessMessages;
FreeOnTerminate := true;
MyTimer:= TTimer.Create(nil);
MyTimer.Interval:=20;
MyTimer.Enabled:=true;
MyTimer.OnTimer:=onMyTimer;
DS := aDS;
CDS:= aCDS;
SC:= aSC;
Form:=Application.FindComponent(aForm);
TProgressBar(Form.FindComponent("Progress")).Visible:=true;
CDS.Close;
if aSQL<>"" then
cds.CommandText := aSQL;
CDS.RemoteServer:=aSC;
Resume;
end;
destructor TMyThread .Destroy;
begin
synchronize(EndWork);
inherited;
end;
procedure TMyThread.DoWork;
begin
DS.DataSet := cds;
TDBGrid(Form.FindComponent("DBGrid")).DataSource:=ds;
end;
procedure TMyThread.EndWork;
begin
if Assigned(CDS) then
begin
cds.Close;
CDS := nil;
end;
if Assigned(ds) then
begin
ds := nil;
end;
if Assigned(SC) then
begin
SC.Close;
SC := nil;
end;
if Assigned(form) then
begin
form := nil;
end;
if Assigned(MyTimer) then
begin
MyTimer.Free;
MyTimer := nil;
end;
end;
procedure TMyThread.Execute;
begin
SC.Open;
cds.Open;
synchronize(DoWork);
MyTimer.Enabled:=false;
TProgressBar(Form.FindComponent("Progress")).Position:=1000;
Sleep(1000);
TProgressBar(Form.FindComponent("Progress")).Position:=0;
TProgressBar(Form.FindComponent("Progress")).Visible:=false;
suspend;
end;
Может как нибудь по другому получать данные?
Чтонибудь типа
CDS.AppendData(QueryData, true);
А вот как получить QueryData...
Что лдя этого подойдет AppServer AS_GetRecords или как надо...
Если у когото работает посоветуйте пож...
← →
BorisUK (2003-05-08 12:54) [3]Да... забыл добавить..
Вызываю вот так...
..
DBGrid.DataSource:=nil;
MyData := TThreadQuery.Create(dm.sConnection, dm.CDS_ref,"SELECT * FROM TableName",dm.DataSource1, "fMain");
..
Указываем какой ClientDataSet с каким DataSource связать и форму на которую выдать результат...
← →
Romkin (2003-05-08 13:08) [4]Повторяю, компоненты, с которыми работает поток, должны быть созданы в контексте данного потока ( в методе execute)
← →
sniknik (2003-05-08 13:13) [5]TDBGrid(Form.FindComponent("DBGrid")).DataSource:=ds;
Application.ProcessMessages;
MyTimer : TTimer;
TProgressBar
????
компоненты из других потоков или имеющие собственный. а ты даже без синхронизации ... хорошо еще что не виснет.
и Sleep(1000); тоже зря, в этом месте как раз работа основная должна идти.
> но чтото некоректно с его завершением... тоесть освобождением всех занятых ресурсов.
а зачем замораживать поток (suspend;) как раз на выходе? как раз правильно что не освобождает, не доходит.
и destructor бледный, не освобождает все что в конструкторе наплодил (хотя до этого не доходит похоже)
в общем бросай все и заново... ;о))
← →
BorisUK (2003-05-08 14:24) [6]Да еслиб в этом дело было то я б быстро исправилсь...
Там не в этом дел....
Вот упростил до невозможности...
Все компоненты должны лежать на форме...
ClientDataSet
DataSource
SocketConnection
И все должны быть как надо связаны...
я вынес
SC.Open;
cds.Open;
в синхронезируемую процедуру DoWork
теперь все работае, но так как открытик набора данных (а не только его передача в компонент визуализации) синхронизируется, то никакой параллельности не ощущается... но зато без ошибок :)
Получили данные... остановили поток (это на вопрос sniknik © (08.05.03 13:13) - зачем тормажу) потом заботаем с данными и когда надо следующий запрос - разрушаем поток..
if Assigned(MyQuery) then
begin
MyQuery.Resume;
MyQuery := nil;
end;
Вот упрощенный код (раньше еще и прогрес рисовали, но в данном случае он всеравно не рисуется, пока DoWork синхронизируется)
constructor TMyThread.Create(aSC : TSocketConnection; aCDS : TClientDataSet;aSQL: String; aDS: TDataSource; aForm: String);
begin
inherited Create(True);
FreeOnTerminate := true;
DS := aDS;
CDS:= aCDS;
SC:= aSC;
CDS.Close;
if aSQL<>"" then
cds.CommandText := aSQL;
CDS.RemoteServer:=aSC;
Resume;
end;
destructor TMyThread .Destroy;
begin
synchronize(EndWork);
inherited;
end;
procedure TMyThread.DoWork;
begin
SC.Open;
cds.Open;
DS.DataSet := cds;
end;
procedure TMyThread.EndWork;
begin
if Assigned(CDS) then
begin
cds.Close;
CDS := nil;
end;
if Assigned(ds) then
begin
ds := nil;
end;
if Assigned(SC) then
begin
SC.Close;
SC := nil;
end;
end;
procedure TMyThread.Execute;
begin
synchronize(DoWork);
suspend;
end;
Так всетаки ошибка гдето в cds.Open;
Подскажите плиз - как грамотно сделать открытие именно ClientDataSet в потоке... ????
(аналогия прошлого кода с обычным Query работает весьма нехило.... А этот кусок хотя и работает корректно - не выполняет своего предназначения параллельности...)
← →
BorisUK (2003-05-08 14:40) [7]<<Romkin © (08.05.03 13:08)
Там же присутствуют экземпляры для них. ...
И для DataBase с Query все работает....
И к тому же для каждого запроса создавать в рантайме SocketConnection для меня не приемлемо...
Я знаю что должно быть можно както так!!!
← →
BorisUK (2003-05-08 15:58) [8]Всеже попробовал все создавать...
Вот так переписал конструктор
Теперь трабл при попытке разорвать соединение
В деструкторе - прога виснет!!!
if Assigned(SC) then
begin
SC.Connected:=false;
SC.Free;
SC := nil;
end;
Вот такой теперь конструктор
constructor TThreadQuery.Create(aSC : TSocketConnection; aCDS : TClientDataSet;aSQL: String; aDS: TDataSource; aForm: String);
begin
inherited Create(True);
FreeOnTerminate := true;
SC:=TSocketConnection.Create(nil);
SC.Address:=aSC.Address;
SC.ServerGUID:=aSC.ServerGUID;
SC.ServerName:=aSC.ServerName;
CDS:=TClientDataSet.Create(nil);
CDS.RemoteServer:=SC;
CDS.ProviderName:=aCDS.ProviderName;
DS:=TDataSource.Create(nil);
DS.DataSet:=cds;
CDS.Close;
if aSQL<>"" then
cds.CommandText := aSQL;
Resume;
end;
← →
Serginio (2003-05-08 16:18) [9]Попробуй
DS.DataSet:=nil;
CDS.Free;
← →
Serginio (2003-05-08 16:21) [10]А лучше передать скопировать данные из потоковой ClientDataSet в
ClientDataSet на форме FormCDS.Data:=CDS.Data;
← →
Romkin (2003-05-08 16:31) [11]Либо ты перестанешь издеваться над менеджером памяти и всем остальным, либо так все и останется
← →
Erik Ivanov (2003-05-09 14:48) [12]Просто нету слов, ты что издеваешся!!!!!!!!!
Тебе же сказали TClientDataSet НЕ THREAD SAFE компонент!
Нельзя так и точка.
Ты хоть знаеш, что с Com работаеш и что такое CoInitialize?
P.S.
С TDataSet кажется можно из потока работать. С TSocketConnection через истерфейсы тожен можно.
← →
BorisUK (2003-05-12 11:32) [13]
> Romkin © (08.05.03 16:31)
Скажи пож .. Что именно не так...?
когда я делаю допустим SC и CDS := T<имя класса>.Create(nil)
Это разве не создание в контексте потока?
И почему " должны быть созданы в контексте данного потока (в методе execute)
" - Это обязательно только там или в конструкторе тоже пойдет?
> Serginio (08.05.03 16:18)
При любой попытке free или Close после выполнения потока прога зависает... Хотя данные в DataSet получаются... в потоке... Но потом При любой попытке обратится к свойствам компонетнов все зависает... а при попытке закрыть приложение говорится что
Ошика "Expected return values not received" ..
> Erik Ivanov (09.05.03 14:48)
Спасибо, я так и думал! Я и просил подсказать - как надо поступать в данном случае... ?
И я знаю что работаю с COM - я об этом писал выше...
<< (08.05.03 09:18) "Естественно я отдаю себе отчет в некоторой разнице между доступом к данным посредством Query и посредством ClientDataSet в трехзвенке через интерфейс провайдера... "
И Я спрашивал (08.05.03 12:50) ".. для этого подойдет AppServer.AS_GetRecords или как надо...?..."
Тоесть вызывать через интерфейс AppServer метод AS_GetRecords и добавлять в цикле в ClientDataSet методом CDS.AppendData...
Но тут тоже есть проблема ... Необходимо чтобы AS_GetRecords возвращала по одной записе (указываем это в параметре) и на серверном наборе указатель не сбрасывался на начало , а постоянно передвигался... Почемуто возможно вызвать AS_GetRecords только с параметром grReset (при вызове с grMetaData, grXML или комбинацией говорит что "Datapacket contains no Meta data."), grReset - как раз и говорит провайдеру на сервере постоянно сбрасывать указатель на начало набора данных... НО у компонента TDataSetProvider есть свойство PoNoReset = true которое (по описанию) как раз и говорит игнорировать grReset и не сбрасывать указатель на начало после каждого вызова AS_GetRecords... Проблема в том, что это работает только если провайдер смотрит на компонент TTable... С TQuery или другими подобными ему указатель постоянно передвигается указатель на начало... а так как у AS_GetRecords нет параметра, указывающего какую конкретно (по номеру например) запись выбрать, то не могу придумать как пробегаться по всем записям.. которые вернул TQuery с помощью AS_GetRecords?
Мастера!! Помогите плиз найти решение - как работать с ClientDataSet в потоке? НУ ОЧЕНЬ НАДО!!!!
← →
Romkin (2003-05-12 12:00) [14]constructor TThreadQuery.Create - это выполняется, разумеется, в контексте главного потока, о чем я и говорю. Ты сначала ведь создаешь TThread, а потом его запускаешь. Контекст твоего потока - только execute. Все, что ты вызываешь изнутри execute должно быть также создано внутри execute
← →
Romkin (2003-05-12 12:05) [15]Не надо мудрить, все, что тебе нужно - отдельный TDatamodule с находящимися в нем TSocketConnection & TClientDataSet. В Execute вызови coInitialize, создай этот модуль, открой датасет, и скопируй синхронно Data в ClientDataSet на форме для отображения результата. Отсоединись и сделай Free для этого модуля, coUninitialize и убери поток. Все!
← →
BorisUK (2003-05-12 14:50) [16]
> Romkin © (12.05.03 12:05)
Спасибо!!!
Заработало....
Жаль только что не так как мне надо...
Я имею ввиду что в данном проекте любой запрос будет приводить к созданию кратковременного соединения с сервером... Данные же будут копироваться в локальный кеш ClientDataSet ...
В некоторых случаях так работать даже правильнее...
Но в моем случае неплохо былобы всеже сессию держать открытой и крутить в потоке только выборку одного из
ClientDataSet"ов...
У меня уже получилось сделать это (как я описал это выше), но метод AS_GetRecords правильно работает только если провайдер на сервере смотрит на TTable...
Подскажите чтолибо пож! Как выйти из такой ситуации "как пробегаться по всем записям.. которые вернул именно TQuery с помощью AS_GetRecords?
"
или другой выход из положения...
← →
Romkin (2003-05-12 15:14) [17]Попробуй, модет и пройдет, если TSocketConnection - общий. Но тогда, плиз, у TRemoteDataModule Threading model должна быть Free (не Apartment)
← →
Romkin (2003-05-12 15:22) [18]Я бы сделал выборку данных в ClientDataSet на сервере, в отдельном потоке, инициализацию работы методом сервера, а возврат данных - backcall. И не напряжно, и с потоками на клиенте не маешься, и соединение одно.
Предупреждая вопрос "Как?":
http://rsdn.ru/article/default.asp?db/callback.xml
http://rsdn.ru/article/default.asp?db/midas.xml
← →
Erik Ivanov (2003-05-12 15:22) [19]Посмотри исходники TCustomClientDataSet, по идее тебе придется писать свой класс. И сделать его thread safe :)
← →
BorisUK (2003-05-13 11:18) [20]
> Romkin © (12.05.03 15:14)
> Romkin © (12.05.03 15:22)
Спасибо огромное....
Вот наконец то все заработало... вынес SocketConnection теперь он общий, а остальное в DataModule... и наворачиваю прогу... теперь все в потоках будет...
За callback тоже огромное спасибо... Как раз искал инфу!
Всем остальным большое спасибо :)
Страницы: 1 вся ветка
Форум: "Базы";
Текущий архив: 2003.06.02;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.01 c