Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Cast thread error or terminate thread can free resource

226 views
Skip to first unread message

Jackie Chang

unread,
Mar 15, 2008, 11:49:11 AM3/15/08
to
{ TMyThread }
procedure TMyThread.Execute;
var
StrList: TStringList;
begin
StrList:= TStringList.Create;
try
while (1=1) do
sleep(1000); {I need cast error from Main From! }
finally
StrList.Free;
end;
end;

Cast thread error from Main From! It's possible?
Or terminate thread can run finally section? How?

Thx!


Peter Below (TeamB)

unread,
Mar 16, 2008, 5:00:37 AM3/16/08
to
Jackie Chang wrote:

You cannot force an exception to be raised inside a thread from outside
the thread.

What you need is a different design for your wait loop. Wait on an
event object (TSimpleEvent) to become signalled, perhaps with a
timeout. The object can then be signalled from outside the thread to
make the wait wake up before the timeout expires.

The following thread class is an example of this design. It contains
some stuff not relevant to the problem, so don't get distracted <g>.

type
{: This class implements a thread that can be managed by the thread
pool. It can also be used outside the pool, however, if it is
managed
by an instance of the TThreadManager class.}
TPoolThread = class(TThread)
private
FIdle: Boolean;
FNext: TPoolThread;
FOnThreadDying: TNotifyEvent;
FQueue: IInterfaceList;
FSignal: TSimpleEvent;
FStandalone: Boolean;
procedure RepoolThread;
procedure RunTasks;
procedure WaitForWork;
protected
procedure DoThreadDying;
procedure Execute; override;
property Standalone: Boolean read FStandalone;
public
constructor Create(AStandalone: Boolean = false); reintroduce;
destructor Destroy; override;
procedure Die;
function Hook(NewNext: TPoolThread): TPoolThread;
procedure SubmitTask(const aExecutor: IExecutor);
function Unhook: TPoolThread;
property Idle: Boolean read FIdle;
property Next: TPoolThread read FNext;
property OnThreadDying: TNotifyEvent read FOnThreadDying write
FOnThreadDying;
end;

{: A worker thread will usually be part of a thread pool (aStandalone =
false) but it can also be used in isolation (aStandalone = true). In
the latter case it will not repool itself when a task completes. <br>
The worker thread has an internal task queue and a signal on which it
will wait while the queue is empty. The thread will self-destruct when
it terminates.}
constructor TPoolThread.Create(AStandalone: Boolean = false);
begin
inherited Create(true);
FreeOnTerminate := true;
FSignal := TSimpleEvent.Create;
FQueue := TInterfaceList.Create;
FIdle := true;
FStandalone := AStandalone;
Resume;
end;

destructor TPoolThread.Destroy;
begin
FreeAndNil(FSignal);
FQueue := nil;
inherited Destroy;
end;

{: Terminates and sets the thread up for destruction}
procedure TPoolThread.Die;
begin
Terminate;
try
FSignal.SetEvent;
except on EAccessViolation do
{There is a very slight chance that the thread has already self-
destructed when we try to set the signal. That could result in
an access violation. Ignore that here. }
end;
end;

{: The OnThreadDying even fires in the context of the thread! If you
need an event fired in
the main threads context use OnTerminate, but keep in mind that this
will only work in a VCL GUI app! }
procedure TPoolThread.DoThreadDying;
begin
if Assigned(FOnThreadDying) then FOnThreadDying(Self);
end;

{: Repeat to wait for tasks and execute them until the thread is
terminated. Any exceptions will be trapped and logged, but they
will terminate the thread. A notification event is fired before the
thread ends up in the Great Bit Bucket Beyond. Note that the tasks
are responsible to trap and handle execptions in their execution, so
the thread itself will not get terminated by those errors. }
procedure TPoolThread.Execute;
const
SSeparator = ': ';
begin
try
while not Terminated do begin
WaitForWork;
if not Terminated then
RunTasks;
if not (Terminated or StandAlone) then
RepoolThread;
end; {if}
except
on E: Exception do
LogManager.Log(E.ClassName + SSeparator+E.Message);
end;
DoThreadDying;
end;

{: Sets the Next property to the passed value and returns the old
value of Next}
function TPoolThread.Hook(NewNext: TPoolThread): TPoolThread;
begin
Result := TPoolThread(InterlockedExchangeObject(FNext, NewNext));
end;

{: Set the threads state to idle and tell the pool manager that the
thread is available for work.}
procedure TPoolThread.RepoolThread;
var
Temp: IThreadPool;
begin
FIdle := true;
Temp := InternalThreadPool;
if Assigned(Temp) and not Terminated then
(Temp as IThreadPoolManager).AddThread(Self);
end;

{: Execute tasks from the queue until the queue runs dry or the thread
is terminated. }
procedure TPoolThread.RunTasks;
var
Intf: IInterface;
begin
repeat
Intf := FQueue.First;
FQueue.Delete(0);
(Intf as IExecutor).Execute;
until (FQueue.Count = 0) or Terminated;
end;

{: Adds the passed task to the threads queue and wakes the thread.}
procedure TPoolThread.SubmitTask(const aExecutor: IExecutor);
begin
Assert(Assigned(aExecutor));
FIdle := false;
FQueue.Add(aExecutor as IInterface);
FSignal.SetEvent;
end;

{: Set the Next property to nil and returns its old value}
function TPoolThread.Unhook: TPoolThread;
begin
Result := TPoolThread(InterlockedExchangeObject(FNext, nil));
end;

procedure TPoolThread.WaitForWork;
begin
FSignal.WaitFor(INFINITE);
FSignal.ResetEvent;
end;

--
Peter Below (TeamB)
Don't be a vampire (http://slash7.com/pages/vampires),
use the newsgroup archives :
http://www.tamaracka.com/search.htm
http://groups.google.com

Jackie Chang

unread,
Mar 17, 2008, 5:25:55 AM3/17/08
to
Thanks!

I use Thread to connect database, for some reason, It's may be talk a long
time! The user can break connect!

{ TMyThread }
procedure TMyThread.Execute;
begin
SqlConn.Connected:= True;
end;

SqlExpr.pas
-----------------------------------------
procedure TSQLConnection.DoConnect;
begin
...
LoginParams := TWideStringList.Create;
...
try
...
{ connect database }
FDBXConnection := ConnectionFactory.GetConnection(ConnectionProps);
{ Its may be talk a long time }

finally
FreeAndNil(MemoryConnectionFactory); // If allocated, free it.
SetCursor(DefaultCursor);
LoginParams.Free;
ConnectionProps.Free;
if ConnectionState = csStateConnecting then // an exception occurred
begin
ConnectionState := csStateClosed;
if Assigned(FDBXConnection) then
FreeAndNil(FDBXConnection)
end;
end;
end;
-----------------------------------------
1.TerminateThread(MyThread.Handle, 1);
Cast memory leak!
2.Wait MyThread destroy. That must wait connect finish!

I can not solve my program!, I hope Delphi have ThreadTerminateException :)

Jackie


Remy Lebeau (TeamB)

unread,
Mar 17, 2008, 2:07:24 PM3/17/08
to

"Jackie Chang" <n...@mail.com.tw> wrote in message
news:47db...@newsgroups.borland.com...

> while (1=1) do

You shold be using the thread's Terminated property instead:

while (not Terminated) do

Then your main thread can call the worker thread's Terminate() method when
needed:

MyThread := TMyThread.Create(False);
//...
MyThread.Terminate;


Gambit


Remy Lebeau (TeamB)

unread,
Mar 17, 2008, 2:11:21 PM3/17/08
to

"Jackie Chang" <n...@mail.com.tw> wrote in message
news:47de...@newsgroups.borland.com...

> I use Thread to connect database, for some reason, It's may be talk a long
> time! The user can break connect!

If the connection is taking a long time to finish, the only ways to force
the thread to terminate prematurely is to either destroy the DB object from
memory (likely resulting in access violations), or use the Win32 API
TerminateThread() function (likely resulting in resource leaks).

> 1.TerminateThread(MyThread.Handle, 1);
> Cast memory leak!

That will likely cause leaks, yes. You are performing a brute-force
termination that ends the thread immediately, not allowing it to perform any
cleanup operations for itself.

> 2.Wait MyThread destroy. That must wait connect finish!

Yes, because it waits for the thread to be terminated first.

> I can not solve my program!, I hope Delphi have ThreadTerminateException
> :)

It does not. Even if it did, itwould not solve your problem anyway.

Gambit


Peter Below (TeamB)

unread,
Mar 17, 2008, 2:45:07 PM3/17/08
to
Jackie Chang wrote:

> Thanks!
>
> I use Thread to connect database, for some reason, It's may be talk a
> long time! The user can break connect!

If the database engine itself does not support canceling a running
query from another thread then you simply cannot do this without
causing problems of some kind. The best option is to simply forget the
thread and let it run. If it ever comes back from the query, ignore the
results.

Jackie Chang

unread,
Mar 18, 2008, 1:34:01 PM3/18/08
to
"Remy Lebeau (TeamB)" <no....@no.spam.com> wrote in message
news:47deb44c$1...@newsgroups.borland.com...

>> I can not solve my program!, I hope Delphi have ThreadTerminateException
>> :)
>
> It does not. Even if it did, itwould not solve your problem anyway.

------------------------------------------------


{ TMyThread }
procedure TMyThread.Execute;
var
StrList: TStringList;
begin
StrList:= TStringList.Create;
try

DoSomeLongTimeProcess(StrList); // catch ThreadTerminateException
finally
StrList.Free;
end;
end;
------------------------------------------------
If thread can catch ThreadTerminateException that cast from OS?
If does have. Its can do perform cleanup operations for itself.
I google to found ThreadAbortException; but it's not for Delphi.
Normality; Most program cleanup operations run quickly,
MainForm can wait for the thread to be terminated.


Jackie Chang

unread,
Mar 18, 2008, 1:40:22 PM3/18/08
to
"Peter Below (TeamB)" <no...@nomail.please> wrote in message
news:xn0fnsut...@newsgroups.borland.com...

> If the database engine itself does not support canceling a running
> query from another thread then you simply cannot do this without
> causing problems of some kind. The best option is to simply forget the
> thread and let it run. If it ever comes back from the query, ignore the
> results.

I can't do that! It's application first connect. Not a query.

Jackie


Peter Below (TeamB)

unread,
Mar 18, 2008, 3:13:45 PM3/18/08
to
Jackie Chang wrote:

Then just give the user a message if the thread does not connect inside
x minutes and then terminate the application *without* trying to free
the thread. There's simply nothing else you can do here.

Remy Lebeau (TeamB)

unread,
Mar 18, 2008, 3:35:55 PM3/18/08
to

"Jackie Chang" <n...@mail.com.tw> wrote in message
news:47df...@newsgroups.borland.com...

> If thread can catch ThreadTerminateException that cast from OS?

As I told you earlier, there is no such exception available from the OS.
There is no way for one thread to throw an exception in the context of
another thread. Exceptions are always local to the thread that is throwing
and catching them. In the example you showed, the only way to get an
exception in the thread is if DoSomeLongTimeProcess() itself throws one on
its own, such as if another thread destroys resources that
DoSomeLongTimeProcess() is using.

> I google to found ThreadAbortException; but it's not for Delphi.

That is specific to .NET only. There is nothing like that in native
languages.


Gambit


0 new messages