![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Выполнение запросов в потоке, с использованием DOA
nobody © (2010-06-21 16:13:00)Вопрос к знатокам:
Почему при выполнении запроса в потоке залипает интерфейс всего приложения, вместо того, чтобы реагировать на действия пользователя во время выполнения запроса?
реализовано в коде следующим образом:
type
TThreadControl = class(TThread)
private
FOracleDataSet: TOracleDataSet;
FOracleQuery: TOracleQuery;
FControl_C: TControl;
FControl_A: TAction;
FGrid: TcxGridDBTableView;
FForm: TForm;
FCollapse: boolean;
FProcessMonitor: TProcessMonitor;
protected
FErrorMsg: string;
procedure Execute; override;
procedure LockControl;
procedure UnLockControl;
procedure ShowErrorMsg;
public
constructor Create(CreateSuspended: Boolean; OracleDataSet: TOracleDataSet;
ProcessMonitor: TProcessMonitor; Control: TControl; Grid: TcxGridDBTableView; Form: TForm; FCollapse: boolean;
LockControl: boolean = true);
overload;
constructor Create(CreateSuspended: Boolean; OracleQuery: TOracleQuery;
ProcessMonitor: TProcessMonitor; Control: TControl; Grid: TcxGridDBTableView; Form: TForm; FCollapse: boolean;
LockControl: boolean = true);
overload;
constructor Create(CreateSuspended: Boolean; OracleDataSet: TOracleDataSet;
ProcessMonitor: TProcessMonitor; Control: TAction; Grid: TcxGridDBTableView; Form: TForm; FCollapse: boolean;
LockControl: boolean = true);
overload;
end;
...
procedure TThreadControl.Execute;
begin
inherited;
try
Synchronize(LockControl);
if FOracleQuery <> nil then
FOracleQuery.Execute;
if FOracleDataSet <> nil then
begin
try
FOracleDataSet.Close;
FOracleDataSet.Open;
except
on E: Exception do
begin
FErrorMsg := E.Message;
Synchronize(ShowErrorMsg);
end;
end;
end;
finally
Synchronize(UnLockControl);
end;
end;
MDI-приложение, дочерние формы загружаются из bpl-модулей.
В каждом модуле есть своя сессия, которая активизируется при необходимости выполнения запроса от этой формы.
Сессия отключается по истечению тайм-аута.
Если нужна еще какая-нить инфа, спрашивайте.
nobody (2010-06-22 17:38:00) [1]вообщем, нашел следующий выход:
запускаю экзешник из проводника, а потом средой подключаюсь к нему в памяти через Attach Process и все хорошо отлаживается и останавливается на всех бейкпоинтах... вот такой финт ушами.)
nobody (2010-06-22 14:45:00) [2]
> Это мы ждем твоих комментариев
Все работает, но под отладкой вываливается куча ошибок и окно CPU
Вот примеры ошибок(идут одна за другой и нет им конца...)
вываливается на этом месте:
ntdll.RtlUnwind:
7C92ABC5 8BFF mov edi,edi
7C92ABC7 55 push ebp
7C92ABC8 8BEC mov ebp,esp
7C92ABCA 81EC7C030000 sub esp,$0000037c
7C92ABD0 A1C8E0977C mov eax,[$7c97e0c8]
->7C92ABD5 56 push esi
Но если запускать без отладчика, то все работает без ошибок.
Да и еще один ньюанс:
Приложение построено по принципу подгружаемых bpl-ек.
БПЛька с датамодулем загружается сразу в авдресное пространство приложения и как раз во время ее загрузки под отладчиком происходят эти ошибки
вот еще порядок загрузки модулей в приложение:
Thread Start: Thread ID: 4468. Process KMS.exe (2680)
Process Start: C:\joe\KMS\bin\KMS.exe. Base Address: $00400000. Process KMS.exe (2680)
Module Load: KMS.exe. No Debug Info. Base Address: $00400000. Process KMS.exe (2680)
Module Load: ntdll.dll. No Debug Info. Base Address: $7C900000. Process KMS.exe (2680)
Module Load: KERNEL32.dll. No Debug Info. Base Address: $7C800000. Process KMS.exe (2680)
Module Load: rtl100.bpl. Has Debug Info. Base Address: $20000000. Process KMS.exe (2680)
Module Load: OLEAUT32.dll. No Debug Info. Base Address: $77110000. Process KMS.exe (2680)
Module Load: ADVAPI32.dll. No Debug Info. Base Address: $77DC0000. Process KMS.exe (2680)
Module Load: RPCRT4.dll. No Debug Info. Base Address: $77E70000. Process KMS.exe (2680)
Module Load: Secur32.dll. No Debug Info. Base Address: $77FE0000. Process KMS.exe (2680)
Module Load: GDI32.dll. No Debug Info. Base Address: $77F10000. Process KMS.exe (2680)
Module Load: USER32.dll. No Debug Info. Base Address: $7E360000. Process KMS.exe (2680)
Module Load: msvcrt.dll. No Debug Info. Base Address: $77C00000. Process KMS.exe (2680)
Module Load: ole32.dll. No Debug Info. Base Address: $774D0000. Process KMS.exe (2680)
Module Load: VERSION.dll. No Debug Info. Base Address: $77BF0000. Process KMS.exe (2680)
Module Load: MPR.dll. No Debug Info. Base Address: $71B00000. Process KMS.exe (2680)
Module Load: IMAGEHLP.dll. No Debug Info. Base Address: $76C80000. Process KMS.exe (2680)
Module Load: WSOCK32.dll. No Debug Info. Base Address: $71AB0000. Process KMS.exe (2680)
Module Load: WS2_32.dll. No Debug Info. Base Address: $71A90000. Process KMS.exe (2680)
Module Load: WS2HELP.dll. No Debug Info. Base Address: $71A80000. Process KMS.exe (2680)
Module Load: OLEACC.dll. No Debug Info. Base Address: $74C40000. Process KMS.exe (2680)
Module Load: MSVCP60.dll. No Debug Info. Base Address: $76050000. Process KMS.exe (2680)
Module Load: vcl100.bpl. Has Debug Info. Base Address: $200E0000. Process KMS.exe (2680)
Module Load: COMCTL32.dll. No Debug Info. Base Address: $5D5B0000. Process KMS.exe (2680)
Module Load: SHELL32.dll. No Debug Info. Base Address: $7C9C0000. Process KMS.exe (2680)
Module Load: SHLWAPI.dll. No Debug Info. Base Address: $77F60000. Process KMS.exe (2680)
Module Load: WINSPOOL.DRV. No Debug Info. Base Address: $72FC0000. Process KMS.exe (2680)
Module Load: comdlg32.dll. No Debug Info. Base Address: $76380000. Process KMS.exe (2680)
Module Load: oledlg.dll. No Debug Info. Base Address: $7DFC0000. Process KMS.exe (2680)
Module Load: DMMain.bpl. Has Debug Info. Base Address: $00680000. Process KMS.exe (2680)
и на этом месте попадает в циклическую ошибку.
---------------------------
Debugger Fault Notification
---------------------------
Project C:\joe\KMS\bin\KMS.exe faulted with message: 'access violation at 0x7c92abd5: write of address 0x00030d24'. Process Stopped. Use Step or Run to continue.
---------------------------
OK
Среда разработки Code Gear 2007 SP3 Architect
Подскажите, что можно с этим сделать? как помочь отладчику не вывалиться на ошибке?
Ega23 (2010-06-22 12:49:00) [3]
> Жду ваших комментариев.
Многа букаф, ниасилил.
Игорь Шевченко (2010-06-22 12:45:00) [4]
> Жду ваших комментариев.
Это мы ждем твоих комментариев
nobody (2010-06-22 12:40:00) [5]Вообщем, нашел старую разработку под Информикс
Переделал ее под Оракл.
Вот что получилось:
procedure TCustomConnectionManager.ExecuteThread(Thread: TThread);
function OtherThreadsExists: Boolean;
begin
FCashLock.Enter;
try
Result := (FCash <> nil) and
(FCash.Count > 1);
finally
FCashLock.Leave;
end;
end;
function WaitExpired(Thread: TCashThread; Timeout: Cardinal): Boolean;
begin
FCashLock.Enter;
try
Thread.Waiting := True;
Thread.Event.ResetEvent;
finally
FCashLock.Leave;
end;
try
Result := Thread.Terminated or
(Thread.Event.WaitFor(Timeout) <> wrSignaled) or
Thread.Terminated;
finally
Thread.Waiting := False; // if Result is True or error
end;
end;
var
CashThread: TCashThread;
Connection: TObject;
CashIndex: Integer;
Task: TConnectionTask;
begin
Connection := nil;
CashThread := Thread as TCashThread;
try
repeat
Task := nil;
repeat
if CashThread.CashSizeChanged and
(CashThread.Series = nil) then
begin
FCashLock.Enter;
try
if FCash.Count > CashSize then
CashThread.Terminate;
finally
FCashLock.Leave;
end;
if CashThread.Terminated then
Break;
end;
FTaskLock.Enter;
try
Task := FFirst;
if CashThread.Series = nil then
while Task <> nil do
begin
if Task.Series = nil then
Break;
FCashLock.Enter;
try
CashIndex := FCash.Count;
repeat
Dec(CashIndex);
until (CashIndex < 0) or
(TCashThread(FCash[CashIndex]).Series = Task.Series);
finally
FCashLock.Leave;
end;
if CashIndex < 0 then
Break;
Task := Task.Next;
end
else
while (Task <> nil) and
(Task.Series <> CashThread.Series) do
Task := Task.Next;
if Task <> nil then
begin
if FFirst = Task then
begin
FFirst := Task.Next;
if FFirst <> nil then
FFirst.Prev := nil;
end
else
Task.Prev.Next := Task.Next;
if FLast = Task then
begin
FLast := Task.Prev;
if FLast <> nil then
FLast.Next := nil;
end
else
Task.Next.Prev := Task.Prev;
if Task is TSeriesStartTask then
CashThread.Series := Task.Series
else
if Task is TSeriesEndTask then
CashThread.Series := nil;
end;
finally
FTaskLock.Leave;
end;
if Task <> nil then
try
Notify(Task, mnStart);
try
if Connection = nil then
try
Connection := CreateConnection;
except
FreeAndNil(Connection);
raise;
end;
if Task is TSeriesStartTask then
StartSeries(Connection, TSeriesStartTask(Task).Transaction)
else
if Task is TSeriesEndTask then
EndSeries(Connection, TSeriesEndTask(Task).Action)
else
ExecuteTask(Connection, Task);
Notify(Task, mnComplete);
except
on E: Exception do
Notify(Task, mnError, E.Message);
end;
finally
Task.Free;
CashThread.Info.TaskExecuted;
end;
until CashThread.Terminated or
((Task = nil) and
WaitExpired(CashThread, IdleTimeout));
until CashThread.Terminated or
OtherThreadsExists or
WaitExpired(CashThread, ActivityTimeout);
finally
FreeAndNil(Connection);
end;
end;
Жду ваших комментариев.
Игорь Шевченко (2010-06-22 11:43:00) [6]Petr V. Abramov © (22.06.10 11:32) [43]
Клиент сам по себе - thread safe, а как его инициализируют, это проблема инициализаторов
Petr V. Abramov (2010-06-22 11:32:00) [7]
> Игорь Шевченко © (21.06.10 17:09) [28]
> клиент Oracle - thread safe.
когда инициализирован в соотв. режиме. У DOA это вынесено в какое-то не помню свойство OracleSession, по умолчанию стоИт не safe.
Скорее всего, в данном случае проблема не только отсюда, но так, о птичках.
Игорь Шевченко (2010-06-21 19:13:00) [8]
> где лочить контролы
TDataSet.DisableControls/EnableControls
nobody (2010-06-21 18:16:00) [9]
> Гугл, Demos и книжки
ну я в гугле живу из-за этого головняка, а сюда случайно зашел в память о былых временах...
Ega23 (2010-06-21 18:10:00) [10]
> вообщем туманно все и конкретных примеров с критической
> секцией никто не привел
Гугл, Demos и книжки - как, отменили?
nobody (2010-06-21 18:09:00) [11]кстати, убрал из метода TThreadControl.Execute
LockControl и UnLockControl и вроде перестало так залипать, но пока непонятно, где лочить контролы, во время выполнения потока ... или сделать это до выполнеия запроса, прямо перед созданием потока?... тогда нужен флаг, который будет сигнализировать завершение потока...
вообщем туманно все и конкретных примеров с критической секцией никто не привел... а как все хорошо начиналось... эх, пойду-нажрусь с горя!
Игорь Шевченко (2010-06-21 17:38:00) [12]Ega23 © (21.06.10 17:18) [32]
> С подключенными DataSource-ами?
> Ну тогда это хорошие компоненты.
Ты не путай божий дар с яичницей. Если у тебя в потоке происходит DataEvent и подключены визуальные компоненты, то все, что пишется в комментариях к TThread про Syncronize, остается в силе. А сами DataSet-ы в потоках вполне могут осуществлять свою функциональность. Например, их можно обходить.
Ega23 (2010-06-21 17:25:00) [13]
> ДОА тоже нормально работает с подключенными датасорсами
> в потоке...
Дык и обычный какой-нибудь TADODataSet может работать в потоке. С подключенными DataSource. Вопрос в том, как контролы себя вести будут. И насколько это целесообразно.
nobody (2010-06-21 17:23:00) [14]
> Например.
Вот я бы тоже и хотел посмотреть на пример использования этой схемы работы с потоком...
nobody (2010-06-21 17:21:00) [15]
> С подключенными DataSource-ами?
> Ну тогда это хорошие компоненты.
ДОА тоже нормально работает с подключенными датасорсами в потоке...
Ega23 (2010-06-21 17:20:00) [16]
> а соответствующие контроллы блокировать, пока не будет получено
> сообщение
> WM_THREAD_DONE?
Например.
nobody (2010-06-21 17:19:00) [17]//DoSomeWork;
- здесь выполнять открытие датасета? а что по поводу memtable? и критических секций по бокам?
Ega23 (2010-06-21 17:18:00) [18]
> а наши компоненты доступа к Oracle вполне спокойно относятся
> к передаче их в поток (ну и манипуляций с ними в потоке)
С подключенными DataSource-ами?
Ну тогда это хорошие компоненты.
nobody (2010-06-21 17:18:00) [19]
> Ega23 © (21.06.10 17:14) [30]
а соответствующие контроллы блокировать, пока не будет получено сообщение
WM_THREAD_DONE?
Ega23 (2010-06-21 17:14:00) [20]
> Покажите кто-нить кусок кода из готового проекта, плиз!
TMyThread = class (TThread)
private
FWND: HWND;
FID: Integer;
function ReportStartExecution: Boolean;
protected
procedure Execute; override;
public
constructor Create(aID: Integer; Wnd: HWND);
procedure DoTerminate; override;
end;
constructor TMyThread.Create(aTaskID: Integer; Wnd: HWND);
begin
inherited Create(True);
FWND := Wnd;
FID := aID;
FreeOnTerminate := True;
end;
function TMyThread.ReportStartExecution: Boolean;
begin
Result := SendMessage(FWND, WM_THREAD_START,
Integer(Self), FID) = WM_RESULT_OK;
end;
procedure TMyThread.DoTerminate;
begin
PostMessage(FWND, WM_THREAD_DONE, Integer(Self), TaskID);
end;
procedure TMyThread.Execute;
begin
try
ReportStartExecution;
//DoSomeWork;
except
//////
end;
end;