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

Вниз

Проблем при использовнии потоков.   Найти похожие ветки 

 
Карелин Артем   (2003-10-07 10:19) [0]

Есть следующий код, при выполнении выполнении которого несколько раз возникает ошибка AV. Что не я делаю неправильно?
type
TSQLCounter = class(TThread)
private
FValue: integer;
FDatabase: TIBDatabase;
FTransaction: TIBTransaction;
FTempQ: TIBQuery;
procedure SetValue(const Value: integer);
procedure SetDatabase(const Value: TIBDatabase);
procedure SetTempQ(const Value: TIBQuery);
procedure SetTransaction(const Value: TIBTransaction);
published
property Value:integer read FValue write FValue;
private
property TempQ:TIBQuery read FTempQ write SetTempQ;
property Database:TIBDatabase read FDatabase write SetDatabase;
property Transaction:TIBTransaction read FTransaction write SetTransaction;
procedure FillQ;
{ Private declarations }
protected
procedure Execute; override;
end;

var
fBigSearch: TfBigSearch;
SQLCounter:TSQLCounter;

procedure TfBigSearch.Button1Click(Sender: TObject);
********
try
if SQLCounter<>nil then TerminateThread(SQLCounter.Handle,0);
finally
end;
SQLCounter:=TSQLCounter.Create(true);
with SQLCounter do
begin
OnTerminate:=OnThreadOff;
FreeOnTerminate:=true;
Priority:=tpLower;
Resume;
end;
********

procedure TSQLCounter.Execute;
begin
Synchronize(FillQ);
TempQ.Open;
Value:=TempQ.Fields[0].AsInteger;
Transaction.Commit;
Database.Close;
TempQ.Free;
Transaction.Free;
Database.Free;
Terminate;
end;

procedure TSQLCounter.FillQ;
begin
fBigSearch.StatusBar1.SimpleText:="Eaao iian?ao ?enea caienae.";
TempQ:=TIBQuery.Create(nil);
Database:=TIBDatabase.Create(nil);
Transaction:=TIBTransaction.Create(nil);
Database.Params.AddStrings(MainData.MainBase.Params);
Database.LoginPrompt:=false;
Database.DatabaseName:=MainData.MainBase.DatabaseName;
Transaction.DefaultDatabase:=Database;
Database.DefaultTransaction:=Transaction;
Database.SQLDialect:=3;
Database.Open;
Transaction.StartTransaction;
TempQ.Database:=Database;
TempQ.Transaction:=Transaction;
TempQ.SQL.Text:=StringReplace(fBigSearch.IBQuery1.SQL.Text,"*","count(*)",[rfReplaceAll]);
ShowMessage(TempQ.SQL.Text);
TempQ.Prepare;
TempQ.Params.Assign(fBigSearch.IBQuery1.Params);
end;

procedure TSQLCounter.SetDatabase(const Value: TIBDatabase);
begin
FDatabase := Value;
end;

procedure TSQLCounter.SetTempQ(const Value: TIBQuery);
begin
FTempQ := Value;
end;

procedure TSQLCounter.SetTransaction(const Value: TIBTransaction);
begin
FTransaction := Value;
end;

procedure TSQLCounter.SetValue(const Value: integer);
begin
FValue := Value;
end;

procedure TfBigSearch.OnThreadOff(Sender: TObject);
begin
if Sender<>SQLCounter then exit;
StatusBar1.SimpleText:="Iaeaaii "+IntToStr((Sender as TSQLCounter).Value)+" caienae.";
end;


 
Карелин Артем   (2003-10-07 10:22) [1]

Что не я делаю неправильно? Читать как Что я делаю неправильно?


 
Verg   (2003-10-07 10:29) [2]

С первого взгляда:

в ф-ции OnThreadOff не хватает SQLCounter:=nil;


 
HolACost!   (2003-10-07 10:30) [3]

Сдаётся мне, что ты провверяешь SQLCounter на nil, а оно у тебя так - где конструктор в котором эта фигня инициализируется или что-то хоть подобное?


 
Digitman   (2003-10-07 10:31) [4]


> Что я делаю неправильно


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

2. Не обрабатываешь потенциально возможные исключения в методе Execute

3. Не приводишь дословный текст сообщения об AV

4. Не предпринимаешь никаких попыток анализа причин AV, не трассируешь свой код


 
pasha_golub   (2003-10-07 11:19) [5]

Я бе еще добавил, что TerminateThread это функция аварийного завершения потока и она после себя мусора оставляет немерянно


 
Polevi   (2003-10-07 11:21) [6]

немерянно это сколько


 
pasha_golub   (2003-10-07 11:42) [7]

Это есть значит не измеряно, то есть не знаю :-))


 
Polevi   (2003-10-07 11:48) [8]

а я знаю
столько ресурсов, сколько поток захватил
так что не надо быть столь категоричным :-)


 
pasha_golub   (2003-10-07 11:58) [9]

2Polevi
Это вопрос спорный :-)


 
Digitman   (2003-10-07 12:01) [10]


> pasha_golub



> Это вопрос спорный


и каковы твои аргументы против утверждения <Polevi> ?


 
pasha_golub   (2003-10-07 12:07) [11]

2Digitman
У меня нет аргументов, но мне кажется что никто не мерял размер потеряной памяти, а следовательно не надо быть таким категоричным :-)


 
Карелин Артем   (2003-10-07 12:13) [12]

Digitman © (07.10.03 10:31) [4]
1)Не понял! Я же создаю обьекты доступа внутри вторичного потока. Поясни свое пост, ежели не трудно.
3)Нарушение доступ в модуле project1.exe по адресу 00000000 попытка чтения 00000000.
4)Вообще-то трассировал.

Ну а насчет TerminateThread: запрос у меня типа select count, выполняется долго и другого способа снять его я не вижу. Метод Terminate будет ждать завершения запроса.


 
Digitman   (2003-10-07 12:16) [13]


> мне кажется что никто не мерял размер потеряной памяти


да и "мерить" ее не надо ... достаточно иметь четкое представление о том, что как происходит при терминировании объектов "поток" и "процесс"

и, кстати, "память" - далеко не единственный ресурс, о котором идет речь в плане ресурсов


 
Verg   (2003-10-07 12:18) [14]


> 1)Не понял! Я же создаю обьекты доступа внутри вторичного
> потока. Поясни свое пост, ежели не трудно.


Не путай понятия. ВНУТРИ - это значит в контексте. Synchronize же как раз привод к тому, что метод в Sync выполняет клавный поток


> Метод Terminate будет ждать завершения запроса.


Не, ничего он ждать не будет


> Ну а насчет TerminateThread: запрос у меня типа select count,
> выполняется долго и другого способа снять его я не вижу.
>


А как тебе нравится хотя бы это?

If the target thread owns a critical section, the critical section will not be released.

Мне этого хватило, чтобы я как бы "забыл" про TerminateThread почти навсегда


 
Polevi   (2003-10-07 12:20) [15]

>Карелин Артем © (07.10.03 12:13) [12]
что по твоему делает Synchronize(FillQ);


 
Digitman   (2003-10-07 12:21) [16]


> 1)...Я же создаю обьекты доступа внутри вторичного
> потока


нет ! поскольку конструкторы их вызываются в контексте исполнения метода Synchronize(), то фактически исполнены они будут в том кодовом потоке, который самым первым вызвал TThread.Create, т.е. в данном случае - осн.код.поток


> 4)Вообще-то трассировал


что трассировал ? только код конструктора или ВСЕ методы потока ?


 
Карелин Артем   (2003-10-07 12:22) [17]

Verg © (07.10.03 12:18) [14]
Понял про контекст, исправлюсь.


 
pasha_golub   (2003-10-07 12:24) [18]

Digitman © (07.10.03 12:16) [13]

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


 
Verg   (2003-10-07 12:25) [19]


> Synchronize(), то фактически исполнены они будут в том кодовом
> потоке, который самым первым вызвал TThread.Create


2Digitman

На всякий случай: По крайней мере с D6 Synchronize методы выполняются ТОЛЬКО главным потоком (MainThread) вне зависимости от того, какой поток выполнил TThread.Create


 
Verg   (2003-10-07 12:28) [20]

Они отказались от исползования ржима QS_SENDMESSAGE очереди для синхронизации. Сделано это было, очевидно, для кроссплатформы.


 
Digitman   (2003-10-07 12:31) [21]


> запрос у меня типа select count, выполняется долго и другого
> способа снять его я не вижу


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

корректные способы "снять" запрос есть.
по кр.мере - в IB/FB/YA
сходи на ibase.ru, там есть статьи на эту тему

терминируя же тред, ты ни коим образом не извещаешь сервер о том, что клиент более не нуждается результатах запроса и не ждет их. Сервер продолжает "молотить" запрос до тех пор, пока не наткнется на исключение тайм-аута и/или дисконнекта соединения с клиентом !


 
Владислав   (2003-10-07 12:38) [22]

> Карелин Артем © (07.10.03 10:19)

На первый взгляд, твоя проблема в этом:

FreeOnTerminate:=true;
...
if SQLCounter<>nil then TerminateThread(SQLCounter.Handle,0);

А вообще, код почти весь проблемный. Начиная с самого простого. Зачем в Execute вот это: Terminate; ?


 
Карелин Артем   (2003-10-07 12:41) [23]

>терминируя же тред, ты ни коим образом не извещаешь сервер о том, что клиент более не нуждается результатах запроса и не ждет их. Сервер продолжает "молотить" запрос до тех пор, пока не наткнется на исключение тайм-аута и/или дисконнекта соединения с клиентом !
Странно, ибо у меня сервер СРАЗУ прекращает выполнять запрос.
Индексы корректные и эффективные, данных может быть очень много.


 
Карелин Артем   (2003-10-07 12:43) [24]

Владислав © (07.10.03 12:38) [22]
Сам не знаю, зачем там это. Наверно справку по этому методу глядел и забыл удалить :(


 
Digitman   (2003-10-07 12:46) [25]


> Verg © (07.10.03 12:25) [19]


мне сложно проверить (не пользую Д6), но есть большие сомнения в этом

представим себе ситуацию - хост-процесс VB-приложения в доп.код.потоке вызвал некую эксп.ф-цию из Delphi-DLL. Ф-ция , разумеется, выполняется в том код.потоке, в котором она была вызвана. В теле ф-ции происходит самый первый вызов TThread.Create. В Д5 при этом создается невиз.окно, которое впоследствии будет как минимум принимать и обрабатывать сообщения синхронизации с вызывающим код.потоком. Без организации этого простейшего и наглядного механизма заставить вызывающий код.поток (не имеющий понятия ни о каких внутренних соглашениях Делфи и VCL !!) по какой-то неведомой ему заранее команде выполнить какую-то неведомую ему подпрограмму - задача весьма нетривиальная... было бы, конечно, прелюбопытно взглягуть, как "изголился" Борланд в Д6/Д7, решая эту задачу в своем обновленном супер-пупер классе TThread (и модуле, где все это действо творится)


 
Verg   (2003-10-07 12:57) [26]

Словами долго объяснять, я тут писал "доклад" про нововведения в D6. Почему-то уже потерто....

Вот, взгляни на два куска кода из classes. Я думаю тебе и так будет все понятно:

Вот такой Synchronize в D6:


var
ProcPosted: Boolean;
SyncList: TList = nil;
ThreadLock: TRTLCriticalSection;
ThreadCount: Integer;

procedure TThread.Synchronize(Method: TThreadMethod);
var
SyncProc: TSyncProc;
begin
if GetCurrentThreadID = MainThreadID then
Method
else
begin
{$IFDEF MSWINDOWS}
SyncProc.Signal := CreateEvent(nil, True, False, nil);
try
{$ENDIF}
{$IFDEF LINUX}
FillChar(SyncProc, SizeOf(SyncProc), 0); // This also initializes the cond_var
{$ENDIF}
EnterCriticalSection(ThreadLock);
try
FSynchronizeException := nil;
FMethod := Method;
SyncProc.Thread := Self;
SyncList.Add(@SyncProc);
ProcPosted := True;
if Assigned(WakeMainThread) then
WakeMainThread(Self);
{$IFDEF MSWINDOWS}
LeaveCriticalSection(ThreadLock);
try
WaitForSingleObject(SyncProc.Signal, INFINITE);
finally
EnterCriticalSection(ThreadLock);
end;
{$ENDIF}
{$IFDEF LINUX}
pthread_cond_wait(SyncProc.Signal, ThreadLock);
{$ENDIF}
finally
LeaveCriticalSection(ThreadLock);
end;
{$IFDEF MSWINDOWS}
finally
CloseHandle(SyncProc.Signal);
end;
{$ENDIF}
if Assigned(FSynchronizeException) then raise FSynchronizeException;
end;
end;


А разруливать этим SyncList-ом должна процедура:

function CheckSynchronize: Boolean;
var
SyncProc: PSyncProc;
begin
if GetCurrentThreadID <> MainThreadID then
raise EThread.CreateResFmt(@SCheckSynchronizeError,
[GetCurrentThreadID]);
if ProcPosted then
begin
EnterCriticalSection(ThreadLock);
try
Result := (SyncList <> nil) and (SyncList.Count > 0);
if Result then
begin
while SyncList.Count > 0 do
begin
SyncProc := SyncList[0];
SyncList.Delete(0);
try
SyncProc.Thread.FMethod;
except
SyncProc.Thread.FSynchronizeException := AcquireExceptionObject;
end;
{$IFDEF MSWINDOWS}
SetEvent(SyncProc.signal);
{$ENDIF}
{$IFDEF LINUX}
pthread_cond_signal(SyncProc.Signal);
{$ENDIF}
end;
ProcPosted := False;
end;
finally
LeaveCriticalSection(ThreadLock);
end;
end else Result := False;
end;


Здесь есть очч-ень глюкавое место, касающееся переменной


 
Digitman   (2003-10-07 12:57) [27]


> Карелин Артем


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

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


 
Карелин Артем   (2003-10-07 12:58) [28]

Вообще на Ibase.ru в факе есть пример есть по доступу к базам из разных потоков, но без принудительного завершения. На королевстве были примеры по завершению запросов, только запросы были внутри хранимых процедур и завершалось это дело при определенном значении генератора.


 
Владислав   (2003-10-07 13:03) [29]

> Digitman © (07.10.03 12:46) [25]

Это действительно так. Метод вызывается напрямую. По его адресу.


 
Verg   (2003-10-07 13:03) [30]

Здесь есть очч-ень глюкавое место, касающееся переменной
WakeMainThread

var
WakeMainThread: TNotifyEvent = nil;

Попробуй ка примени ее так, как советуют в help-е


Allows background threads to synchronize their execution with the main thread.

Unit

Classes

Category

thread management routines

function CheckSynchronize: Boolean;

Description

It is not necessary to call CheckSynchronize in a GUI application. The call to CheckSynchronize is made automatically by the application object. In a non-GUI application, you must call CheckSynchronize if you use the Synchronize method of TThread. To do this, set the WakeMainThread variable to a procedure that calls CheckSynchronize. CheckSynchronize allows background threads to synchronize their execution with the main thread, so that it is safe to make method calls in the background thread.

CheckSynchronize returns True if a method was synchronized, False if it does nothing.


Короче, грохнется все с AV, если написать так, как они советуют


 
Verg   (2003-10-07 13:03) [31]

Здесь есть очч-ень глюкавое место, касающееся переменной
WakeMainThread

var
WakeMainThread: TNotifyEvent = nil;

Попробуй ка примени ее так, как советуют в help-е


Allows background threads to synchronize their execution with the main thread.

Unit

Classes

Category

thread management routines

function CheckSynchronize: Boolean;

Description

It is not necessary to call CheckSynchronize in a GUI application. The call to CheckSynchronize is made automatically by the application object. In a non-GUI application, you must call CheckSynchronize if you use the Synchronize method of TThread. To do this, set the WakeMainThread variable to a procedure that calls CheckSynchronize. CheckSynchronize allows background threads to synchronize their execution with the main thread, so that it is safe to make method calls in the background thread.

CheckSynchronize returns True if a method was synchronized, False if it does nothing.


Короче, грохнется все с AV, если написать так, как они советуют


 
Polevi   (2003-10-07 13:06) [32]

>Digitman © (07.10.03 12:46) [25]

procedure TApplication.WakeMainThread(Sender: TObject);
begin
PostMessage(Handle, WM_NULL, 0, 0);
end;

в конструкторе TApplication
Classes.WakeMainThread := WakeMainThread;

{$IFDEF MSWINDOWS}
SyncProc.Signal := CreateEvent(nil, True, False, nil);
try
EnterCriticalSection(ThreadLock);
try
FSynchronizeException := nil;
FMethod := Method;
SyncProc.Thread := Self;
SyncList.Add(@SyncProc);
ProcPosted := True;
if Assigned(WakeMainThread) then
WakeMainThread(Self);
LeaveCriticalSection(ThreadLock);
try
WaitForSingleObject(SyncProc.Signal, INFINITE);
finally
EnterCriticalSection(ThreadLock);
end;
finally
LeaveCriticalSection(ThreadLock);
end;
finally
CloseHandle(SyncProc.Signal);
end;

procedure TApplication.Idle(const Msg: TMsg);
var
Control: TControl;
Done: Boolean;
begin
...
...
if (GetCurrentThreadID = MainThreadID) and CheckSynchronize
...
...
end;

function CheckSynchronize: Boolean;
begin
...
...
while SyncList.Count > 0 do
begin
SyncProc := SyncList[0];
SyncList.Delete(0);
try
SyncProc.Thread.FMethod;
except
SyncProc.Thread.FSynchronizeException := AcquireExceptionObject;
end;
SetEvent(SyncProc.signal);


 
Digitman   (2003-10-07 13:07) [33]


> Verg


любопытно бы было еще взглянуть на текст WakeMainThread() и на инициализацию MainThreadId в контексте DLL

в Д5 переменная MainThreadId в DLL инициализируется ид-ром того потока, который грузит эту DLL (в случае, разумеется, сборки ее с RTP , в то время как хост-приложение может быть любым, даже не Делфи-приложением, и собранным с любыми опциями)


 
Polevi   (2003-10-07 13:07) [34]

упс

после
Classes.WakeMainThread := WakeMainThread;
идет код TThread.Synchronize


 
Владислав   (2003-10-07 13:09) [35]

> Verg © (07.10.03 13:03) [30]

"Короче, грохнется все с AV, если написать так, как они советуют"

Это почему? Какие тому причины?


 
Digitman   (2003-10-07 13:11) [36]

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


 
Digitman   (2003-10-07 13:15) [37]


> Владислав © (07.10.03 13:03) [29]
> > Digitman © (07.10.03 12:46) [25]
>
> Это действительно так. Метод вызывается напрямую. По его
> адресу.


а иначе и быть не может) ... как еще вызвать подпрограмму, не зная ее адреса в памяти ?

совсем иной вопрос, КАКОЙ поток это сделает


 
Verg   (2003-10-07 13:16) [38]

Вот смотри.

Неглавный поток с FreeOnTerminate выполняет Synchronize
Кусок из Syncronize..

SyncProc.Thread := Self; // Обрати внимание!!!
SyncList.Add(@SyncProc); // !!!!!!!!
ProcPosted := True;
if Assigned(WakeMainThread) then
WakeMainThread(Self); // А в этой процедуре
мы вызовем CheckSinchronize, да пусть даже и не его, а просто в этой процедуре возникло исключение.

Из-за этого исключения наш поток завершит свою работу, а в силу FreeOnTerminate=true будет уничтожен как объект.
Далее, главный поток продолжает выполнять CheckSinchronize
Ха... А в SyncList-е сидит уже уничтоженный объект..


 
Владислав   (2003-10-07 13:17) [39]

> Digitman © (07.10.03 13:11) [36]

Ибо нефиг потоки в библиотеке плодить :)


 
Владислав   (2003-10-07 13:22) [40]

Он не уничтожиться, пока не выйдет из процедуры Synchronize.
А он из нее не выйдет, пока не закончиться CheckSynchronize.
Там критическая секция стоит.



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

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

Наверх





Память: 0.57 MB
Время: 0.01 c
14-65620
Denwill
2003-10-01 07:23
2003.10.20
Где программисту заработать?


6-65566
oduvan
2003-08-24 17:34
2003.10.20
Как проверить, что все данные получитны сокетом?


1-65530
Serguar
2003-10-09 12:34
2003.10.20
Как узнать что произошла смена суток


1-65472
webpauk
2003-10-08 17:10
2003.10.20
PopupMenu Height


14-65574
Igit
2003-10-01 07:45
2003.10.20
как использовать jpg





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