Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2002.11.04;
Скачать: CL | DM;

Вниз

DLL & Threads   Найти похожие ветки 

 
Dynamit   (2002-10-22 16:57) [0]

Может знает кто, помогите. Проблема такая - в DLL создается поток в котором происходит запрос к БД. Сигнал готовности - сообщение приложению. В принципе все отрабатывает, кроме выгрузки библиотеки. Создание потока происходит таким образом:


Function QueryExec(AHndl: THandle; Number: Integer; ASQL, AFNum: PChar): THandle; SafeCall;
var
QueryForm: TFQThread;
begin
Application.Handle:=AHndl;
QueryForm := TFQThread.Create(Application);
QueryForm.Visible:=False;

With TQueryThread.Create(QueryForm) Do
Begin
Result:=ThreadId;
QueryForm.UNum:=Number;
QueryForm.USQL:=String(ASQL);
QueryForm.UFName:=String(AFNum);
QueryForm.UHndl:=AHndl;
OnTerminate:=Term;
Resume;
End;
//QueryForm.Free;
end;


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

Метод Create для TThread:


constructor TQueryThread.Create(AQueryForm: TFQThread);
begin
QueryForm := AQueryForm;
FreeOnTerminate := True;
inherited Create(True);
end;


????


 
Игорь Шевченко ©   (2002-10-22 17:22) [1]

А Execute для TThread ?


 
Bis   (2002-10-22 17:27) [2]

ты кажется дважды пытаешь освободить поток
выбирай или FreeOnTerminate или сам вызывай Free


 
NailS ©   (2002-10-22 17:53) [3]


> Если раскоментировать освобождение QueryForm - будет ошибка
> при выполнении тела потока.

Не удивительно.
У тебя же поток стал работать после Resume, и небось в работе использует QueryForm, которой ты следом за вызовом Resume делаешь QueryForm.Free.

QueryExec - это функция экспортируемая из длл?


 
Набережных С.   (2002-10-22 17:57) [4]

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


 
Dynamit   (2002-10-23 08:37) [5]


> Игорь Шевченко


Вот метод Execute:

procedure TQueryThread.Execute;
begin
Try
try
with QueryForm do
begin
SThr.SessionName := Format("A%s%x", [SThr.Name,UNum]);
DB1.SessionName := SThr.SessionName;
DB1.DatabaseName := Format("A%s%x", [DB1.Name,UNum]);
QInd.SessionName := DB1.SessionName;
QInd.DatabaseName := DB1.DatabaseName;
QInd.SQL.Text:=USQL;
If (Not Terminated) Then
Begin
Table1.TableName:=Format(UFName,[UNum]);
Table1.SessionName:=SThr.SessionName;
CrtRcv; // Создание таблицы-приемника
UCnt:=BathMv; // Получение данных
SendMsg(PM_DATAREADY,UNum,UCnt) //Данные готовы
End;
End;
except
on E: Exception do
begin
{ Display any error we receive on the status line }
MessageText := Format("Хмм "+QueryForm.QInd.SQL.Text+#13+" %s: %s.", [E.ClassName, E.Message]);
Synchronize(DisplayMessage);
end;
End;
Finally
QueryForm.QInd.Close;
QueryForm.SThr.Active:=False;
QueryForm.DB1.Connected:=False;
End;
end;



> Bis (22.10.02 17:27)
> ты кажется дважды пытаешь освободить поток
> выбирай или FreeOnTerminate или сам вызывай Free


Я освобождаю форму, а не поток.


> NailS


Дк я ведь согласен, но делать то что-то надо, а освобождал форму только ради эксперимента, ведь где-то ее надо освобождать!


> Набережных С.


Из метода Execute для потока (приведено выше), видно, что я ловлю сообщение о готовности данных. Вот по его приходу я жду еще Sleep(100), и только затем выгружаю библиотеку. (Кстати насколько верно утверждение, что после завершения метода Execute освобождается сам объект потока?).

Так что, пока ситуация темна как ночь!


 
Bis   (2002-10-23 09:11) [6]

Я тут не то в прошлый раз написал, каюсь

Про освобождение формы уже писали, у тебя она автоматически убъется Application. Если хочешь сам, то в методе OnClose формы поставь значение caFreе


 
panov ©   (2002-10-23 09:37) [7]

QueryForm.Free; вызывай в коде Execute - в конце.

With TQueryThread.Create(QueryForm) Do
Begin
Result:=ThreadId;
QueryForm.UNum:=Number;
QueryForm.USQL:=String(ASQL);
QueryForm.UFName:=String(AFNum);
QueryForm.UHndl:=AHndl;
OnTerminate:=Term;
Resume;
End;
QueryForm.Free;

//здесь у тебя форма уничтожается до окончания работы потока, поэтому и возникает exception



 
Dynamit   (2002-10-23 09:56) [8]


> panov

Еще раз повторяю - это для експеримента, потому и закоментировано.


> Bis

caFree не помогает :(


 
panov ©   (2002-10-23 10:06) [9]

>Dynamit (23.10.02 09:56)
Прочитай внимательно то, что написано у меня...
Уничтожай форму в методе Execute, а не в основном потоке.


 
Bis   (2002-10-23 10:22) [10]

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


 
Dynamit   (2002-10-23 10:28) [11]


> panov

Извини, начало просмотрел :)

Но все равно нет в жизни счастья - глюк остался.

Есть у меня еще одна мысль - ведь в функции QueryExec переменная QueryForm - локальная, а значит по завершении будет недоступна...

Нет, перенес ее во внешний блок Var - глобальный для DLL - глюк остался.


 
Dynamit   (2002-10-23 10:30) [12]


> Bis

Уже сделал - результат тот же. Главное ведь что обидно - данные на экране я получаю, а работать могу только до FreeLibrary :(


 
Набережных С.   (2002-10-23 16:21) [13]

Вынужден настаивать(хотя, разумеется, могу и ошибиться) - наиболее вероятно, что при освобождении DLL ошибка возникает из-за того, что код, находящийся в библиотеке, в это время выполняется. "жду еще Sleep(100)" - не понятно, где. Лучше покажи, как ждешь. А ждать лучше не сообщения, а изменения состояния хендла - это гарантирует окончание потока. И форму можно освободить по окончании ожидания. Но возникновение OnTerminate, как и факт уничтожения TThread вызовом деструктора по флагу FreeOnTerminate - еще не гарантия, что код потока завершился. Точнее, обработчик OnTerminate вызывается, когда поток еще исполняется, но после возврата из Execute. Единственная, повторюсь, гарантия завершения кода потока - ожидание хендла(можно и через TThread.WaitFor, хотя имхо лучше через API).

>Кстати насколько верно утверждение, что после завершения метода Execute освобождается сам объект потока?
Дельфийская реализация гарантирует вызов деструктора TThread по окончании Execute, если FreeOnTerminate = true, но это еще не гарантия завершения потока в понимании ОС. Далее следует еще выполнение некоторого завершающего кода в контексте потока.



 
Dynamit   (2002-10-23 16:37) [14]


> Набережных С.


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

procedure TForm1.Button2Click(Sender: TObject);
Var
LibHandle: THandle;
QExec: TQueryExec;
CurHndl: LongInt;
begin
LibHandle:=LoadLibrary("ASQuery.Dll");
Try
If LibHandle=0 Then
Raise EDLLLoadError.Create("Не могу загрузить DLL");
@QExec:=GetProcAddress(LibHandle,"QueryExec");
If Not (@QExec=nil) Then
CurHndl:=QExec(Handle, 1, PChar(Query), PChar("Tmp\Proba.db"));
Button2.Enabled:=False;
Button1.Enabled:=True;
fReady:=False;
While Not fReady Do
Begin
Application.ProcessMessages;
Sleep(100);
End;
Sleep(10);
@QExec:=nil;
Table1.Open;
Finally
FreeLibrary(LibHandle);
End;
end;


Это тестовый вариант, так что строго не судите ;)


 
Набережных С.   (2002-10-23 18:04) [15]

Например, так:

Function QueryExec(AHndl: THandle; Number: Integer; ASQL, AFNum: PChar): THandle; SafeCall
begin
...
With TQueryThread.Create(QueryForm) Do
Begin
Result:=Handle;
....
end;

procedure TForm1.Button2Click(Sender: TObject);
Var
LibHandle: THandle;
QExec: TQueryExec;
CurHndl: LongInt;

HRes: integer;

begin
LibHandle:=LoadLibrary("ASQuery.Dll");
Try
If LibHandle=0 Then
Raise EDLLLoadError.Create("Не могу загрузить DLL");
@QExec:=GetProcAddress(LibHandle,"QueryExec");
If @QExec <> nil Then

begin
CurHndl:=QExec(Handle, 1, PChar(Query), PChar("Tmp\Proba.db"));
Button2.Enabled:=False;
Button1.Enabled:=True;
fReady:=False;

repeat
Application.ProcessMessages;
HRes:=WaitForSingleObject(CurHandle,100);
until (HRes <> WAIT_TIMEOUT) or fReady;
if HRes = WAIT_ABANDONED then RaiseLastWin32Error
else if fReady then (отменено)

@QExec:=nil;
Table1.Open;
end;
Finally
FreeLibrary(LibHandle);
End;
end;



 
Dynamit   (2002-10-24 08:55) [16]

При выгрузке библиотеки осталась ошибка Access violation.

Но был странный момент - не знаю связано это или нет:

При первом запуске после загрузки компа при появлении данных в таблице (Table1.Open) ошибок не было, и можно было работать с таблицей, но при закрытии библиотеки произошла ошибка доступа (Access violation) с зависанием проги (была закрыта с помощью диспетчера). При последующих запусках программа зависала при выгрузке библиотеки (т.е. таблицу я видел, но сделать ничего не успевал). Поставил showMessage после FreeLibrary(LibHandle) - ошибку выдал раньше, чем мое сообщение.

Есть идея попробовать блок Try ... Exception End для удаления ошибки - но ведь это не правильно!

Других идей нет :( и все опускается вместе с руками!


 
Dynamit   (2002-10-24 09:35) [17]

Ураааа!

Все заработало. Объединение последнего совета Набережных С. с предудущими советами (не всеми), пара тройка десятков опытов - вот и все, что нужно было для успеха.

Всем участникам - огромное человеческое спасибо!

Отдельное спасибо господину Набережных С.

До встречи.



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

Текущий архив: 2002.11.04;
Скачать: CL | DM;

Наверх




Память: 0.52 MB
Время: 0.016 c
3-11131
Юра
2002-10-16 14:23
2002.11.04
Нарисовать разделитель в DBGrid


1-11293
dihlos
2002-10-22 19:54
2002.11.04
Скрыть свойства в инспекторе объектов


1-11327
Dynamit
2002-10-22 16:57
2002.11.04
DLL & Threads


1-11149
volph777
2002-10-24 12:26
2002.11.04
конвертирование даты


1-11213
Zergling
2002-10-25 07:09
2002.11.04
TDateTime,SmallInt из D4 в D7 (чтение из типизированного фйла)