Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 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.009 c
14-3109
Style
2003-05-14 11:34
2003.06.02
Delphi Challenge - Народ добавил раздел конкурсы и раздел ссылки


14-3064
lia
2003-05-16 08:39
2003.06.02
Пример реализации SMTP, POP клиента на паскале...


3-2752
АТ
2003-05-07 04:38
2003.06.02
Помогите подключиться к Exel через ADO


1-2864
CABBA
2003-05-22 12:16
2003.06.02
Как делать свои формы?


7-3191
Сергей
2003-04-01 09:46
2003.06.02
Как отключить/включить устройство?





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