I am in a multi-threaded situation and I have a function that I want to be run from only one thread at a time. However, rather than serializing the function in the tradition manner, I want any threads that attempt to enter the function whilst the first thread is running it to return immediately. I do not want the second thread to wait for the first thread.
Here is my code:
function InitMutex(const Name:String; var Handle: THandle):Boolean;
begin
Handle := CreateMutexA(NIL, True, PAnsiChar(Name));
Result := not (GetLastError = ERROR_ALREADY_EXISTS);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
mHandle: THandle;
begin
if not InitMutex(BalloonTipMutex, mHandle) then Exit;
MessageBox(0, 'Executing Code....', '', 0);
ReleaseMutex(mHandle);
CloseHandle(mHandle);
end;
This is just an example with the same problem, cause I couldn't do a test sample with the threads.
The problem is: I click button1 for the first time, The messagebox appears, while the messagebox is still displayed (suppose the function is still running) I press button1 again, nothing is displayed (which is what's supposed to happen) but when I close the message box and press the button again, it shows nothing. (the function supposed to run again since its not running :S)
Try this instead:
procedure TForm1.Button1Click(Sender: TObject);
var mHandle: THandle;
begin
mHandle := 0;
if InitMutex(BalloonTipMutex, mHandle) then
begin
MessageBox(0, 'Executing Code....', '', 0);
ReleaseMutex(mHandle);
end;
if handle <> 0 then
CloseHandle(mHandle);
end;
your problem is... Even if CreateMutex returns error ERROR_ALREADY_EXISTS, it did "open" the mutex. So when your first function exit, the mutex is not freed since your 2nd call opened it, but never closed it. So when you try to call your function a 3rd time, it fails not because your first call kept the mutex open, but because your 2nd call did.
Also, I think InitMutex should return Result := (Handle <> 0) and not (GetLastError = ERROR_ALREADY_EXISTS)
EDIT: On a side note, this isn't really the way mutex are meant to be used. The "traditional" way to use mutex is to create them, then have your thread try to get ownership of them when you want to execute the code protected by the mutex. I would expect CreateMutex to be quite a bit slower than just taking ownership of a mutex and maybe there are some other pitfalls to that technique.
Now that I finally understand the question, I believe that the most efficient solution is to use interlocked operations.
procedure OneAtATimeThroughHere;
//FLockCount is a properly aligned integer, shared between all threads
var
ThisLockCount: Integer;
begin
ThisLockCount := InterlockedIncrement(FLockCount);
try
if ThisLockCount=1 then//we won the race
begin
//do stuff
end;
finally
InterlockedDecrement(FLockCount);
end;
end;
This approach will not permit re-entrant calls. If you need to cater for re-entrant calls then the solution is to use TryEnterCriticalSection(). Critical sections are much easier to use than mutexes, and they are faster too. Delphi wraps up the critical section API in the TCriticalSection object in the SyncObjs unit.
So your code would look like this:
procedure OneAtATimeThroughHere;
//FLock is an instance of TCriticalSection shared between all threads
if FLock.TryEnter then
begin
try
//do stuff
finally
FLock.Release;
end;
end;
As an alternate solution, you could use the AddAtom(), FindAtom() and DeleteAtom() Windows API functions (see: http://msdn.microsoft.com/en-us/library/ms649056(v=vs.85).aspx). There are also global versions of these for use between processes.
Using atoms would allow you to maintain full control over the flow of your threads and contain the entire locking mechanism within the function (like you could with a critical section).
You should create the mutex once and hold on to it for as long as your threads are running, and then have the function use WaitForSingleObject() with a timeout of 0 milliseconds to try to acquire the mutex lock. If WaitForSingleObject() returns WAIT_OBJECT_0, then the function was not already running yet.
var
mHandle: THandle = 0;
procedure TForm1.FormCreate(Sender: TObject);
begin
mHandle := CreateMutex(nil, False, nil);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
CloseHandle(mHandle);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if WaitForSingleObject(mHandle, 0) = WAIT_OBJECT_0 then
begin
try
MessageBox(0, 'Executing Code....', '', 0);
finally
ReleaseMutex(mHandle);
end;
end;
end;
Related
I have a thread that loops performing work and sleeping via an event for a good bit of time (several seconds.) The user can terminate the app while the thread is asleep waiting on the event. What actually happens to the event in that case? Does the thread keep running until the event times out, delaying the full termination of the app? Or does it return immediately with a return value other than timeout?
While it is not impossible to start processes that outlive the Delphi app that created them, threads created and started in the usual manner (via TThread.Create) end as soon as the main thread ends, without waiting for a cycle to complete and without holding up the termination of the main app.
To convince yourself of this, create a Delphi app and a thread that is started by clicking a button. Have the thread write to a file, then sleep for an arbitrarily large amount of time. When you're sure it's asleep, kill the app. Note that the app terminates immediately, and also the thread is "killed in its sleep".
In fact, if we add a finalization section:
type
TTThread = class(TThread)
msg : string;
ctr : Integer;
public procedure Execute; override;
procedure Update;
end;
procedure TTThread.Execute;
var F: TextFile;
begin
ctr := 0;
assignFile(F, 'log.txt');
rewrite(f);
closefile(f);
while not Terminated do
begin
inc(ctr);
msg := 'Updating';
Synchronize(Update);
assignFile(F, 'log.txt');
append(f);
writeln(F, IntToStr(Ctr));
closefile(f);
msg := 'Sleeping';
Synchronize(Update);
Sleep(3000);
end;
end;
procedure TTThread.Update;
begin
Form1.Caption := '**********'+msg+IntToStr(ctr)+'******************';
end;
var T: TTThread;
procedure TForm1.Button1Click(Sender: TObject);
begin
if not assigned(T) then T := TTThread.Create(false);
end;
var F: TextFile;
initialization
finalization
assignFile(F, 'log.txt');
append(f);
writeln(F, 'Application out!');
closefile(f);
end.
(N.B. this is not presented as thread-safe code but it is useful for experimentation.)
You can set your debugger at the last closeFile in the finalization and park it there as long as you like and the thread will not write to the file again. This tells us that by the time the finalization code hits, owned threads are already dead.
Depending on your level of interest, you can also check in the OnClose of the form, override the Close method for your main form, etc.
This leaves some holes in our understanding, like whether the main form formally terminates each thread, but I trust from this you can figure out how you would test that. (A quick scan of the Form source was not especially revealing.)
Background: I need to perform checks whether a bunch of network drives or remote computers are available. Since each DirectoryExists() needs a lot of time until a potential timeout, I perform the checks in separate threads. It can happen, that an end-user closes the application while some of the checks are still running. Since DirectoryExists() blocks, I have no chance of using the classical while not Terminated approach.
procedure TMyThread.Execute;
begin
AExists := DirectoryExists(AFilepath);
end;
Question 1: Is it a problem that some threads are still running when the application quits? Will Windows simply tidy up after me and that's it? Inside the IDE I get notification of un-freed objects, but outside IDE it just appears to be peaceful.
Question 2: Is it possible to terminate such simple threads with TerminateThread or is this potentially harmful in THIS case?
Question 3: I usually take the results from the threads in OnTerminate() event and let the threads FreeOnTerminate afterwards. If I wanted to free them myself, when should I do it? Can I free a thread in its OnTerminate event or is this a tiny bit too early? How would a thread inform me that it is done if not with OnTerminate?
Is it a problem that some threads are still running when the application quits?
Possibly, yes. It depends on what your code does after DirectoryExists() exits. You might end up trying to access things that no longer exist.
Will Windows simply tidy up after me and that's it?
To ensure everything is cleaned up properly, you are responsible for terminating your own threads. When the main VCL thread is done running, it will call ExitProcess(), which will forcibly terminate any secondary threads that are still running, which will not allow them to clean up after themselves, or notify any loaded DLLs that they are being detached from the threads.
Is it possible to terminate such simple threads with TerminateThread or is this potentially harmful in THIS case?
TerminateThread() is ALWAYS potentially harmful. NEVER use it.
I usually take the results from the Threads in OnTerminate() event and let the threads FreeOnTerminate afterwards.
That will not work if the main message loop has exited before the thread terminates. By default, the TThread.OnTerminate event is fired via a call to TThread.Synchronize(). Once the main message loop stops running, there won't be anything to process the pending Synchronize() requests, unless you run your own loop at app exit to call the RTL's CheckSynchronize() procedure until all of your threads have fully terminated.
if I wanted to free them myself, when should I do it?
Before your app wants to exit.
Can I free a thread in its OnTerminate event
No.
or is this a tiny bit too early?
That, and because it is always unsafe to free an object inside an event fired by that same object. The RTL still needs access to the object after the event handler exits.
That being said, since you don't have a clean way to terminate the threads safely, I suggest NOT allowing your app to exit when there are threads still running. When the user requests the app to exit, check if there are threads running, and if so then display a busy UI to the user, wait for all of the threads to terminate, and then exit the app.
For example:
constructor TMyThread.Create(...);
begin
inherited Create(False);
FreeOnTerminate := True;
...
end;
procedure TMyThread.Execute;
begin
...
if Terminated then Exit;
AExists := DirectoryExists(AFilepath);
if Terminated then Exit;
...
end;
type
TMainForm = class(TForm)
...
procedure FormClose(Sender: TObject; var Action: TCloseAction);
...
private
ThreadsRunning: Integer;
procedure StartAThread;
procedure ThreadTerminated(Sender: TObject);
...
end;
...
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if ThreadsRunning = 0 then Exit;
// signal threads to terminate themselves...
if CheckWin32Version(6) then
ShutdownBlockReasonCreate(Handle, 'Waiting for Threads to Terminate');
try
// display busy UI to user ...
repeat
case MsgWaitForMultipleObjects(1, System.Classes.SyncEvent, False, INFINITE, QS_ALLINPUT) of
WAIT_OBJECT_0 : CheckSynchronize;
WAIT_OBJECT_0+1 : Application.ProcessMessages;
WAIT_FAILED : RaiseLastOSError;
end;
until ThreadsRunning = 0;
// hide busy UI ...
finally
if CheckWin32Version(6) then
ShutdownBlockReasonDestroy(Handle);
end;
end;
procedure TMainForm.StartAThread;
var
Thread: TMyThread;
begin
Thread := TMyThread.Create(...);
Thread.OnTerminate := ThreadTerminated;
Thread.Start;
Inc(ThreadsRunning);
end;
procedure TMainForm.ThreadTerminated(Sender: TObject);
begin
Dec(ThreadsRunning);
...
end;
Alternatively:
type
TMainForm = class(TForm)
...
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
...
private
ThreadsRunning: Integer;
WaitingForClose: Boolean;
procedure StartAThread;
procedure ThreadTerminated(Sender: TObject);
...
end;
...
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose := (ThreadsRunning = 0);
if CanClose or WaitingForClose then Exit;
// signal threads to terminate themselves...
WaitingForClose := True;
// display busy UI to user ...
if CheckWin32Version(6) then
ShutdownBlockReasonCreate(Handle, 'Waiting for Threads to Terminate');
end;
procedure TMainForm.StartAThread;
var
Thread: TMyThread;
begin
Thread := TMyThread.Create(...);
Thread.OnTerminate := ThreadTerminated;
Thread.Start;
Inc(ThreadsRunning);
end;
procedure TMainForm.ThreadTerminated(Sender: TObject);
begin
Dec(ThreadsRunning);
...
if WaitingForClose and (ThreadsRunning = 0) then
begin
WaitingForClose := False;
// hide busy UI ...
if CheckWin32Version(6) then
ShutdownBlockReasonDestroy(Handle);
Close;
end;
end;
Is it a problem that some threads are still running when the application quits?
When taken literally, this question is a little bit malformed. That is because after ExitProcess is called, which is how a Delphi application is ended by default, no threads are running.
The answer to the question "is it a problem that some threads didn't have a chance to finish" depends on what these threads failed to complete. You would have to carefully analyze thread code, but generally speaking this might be prone to errors.
Will Windows simply tidy up after me and that's it? Inside the IDE I get notification of un-freed objects, but outside IDE it just appears
to be peaceful.
The OS will reclaim allocated memory when the process address space is destroyed, all object handles will be closed when the process handle table is destroyed, entry points of all loaded libraries will be called with DLL_PROCESS_DETACH. I can't find any documentation on this but I also presume pending IO requests would be called to cancel.
But all of this does not mean there won't be any problems. Things can get messy, for instance, involving interprocess communications or synchronization objects. Documentation for ExitProcess details one such example: if a thread vanishes before releasing a lock that one of the libraries tries to acquire while detaching, there's a deadlock. This blog post gives another specific example where the exiting process is forcibly terminated by the OS if a thread attempts to enter a critical section that is orphaned by another already terminated thread.
While it may make sense to let go of resource releasing at exit time, particularly if cleanup is taking a considerable amount of time, it is possible to get it wrong for a non-trivial application. A robust strategy is to clean up everything before ExitProcess is called. OTOH if you find yourself in a situation where ExitProcess is already called, such as the process is detaching from your dll because of termination, the nearly only safe thing to do is to leave everything behind and return - every other dll could have already been unloaded and every other thread terminated.
Is it possible to terminate such simple threads with TerminateThread or is this potentially harmful in THIS case?
TerminateThread is advised to be used only in most extreme cases but since the question has a bold "THIS" what the code really does should be examined. Looking at the RTL code we can see that the worst that can happen is leaving a file handle open which is accessed for reading only. THIS is not a problem at process termination time since the handle will be closed shortly.
I usually take the results from the threads in OnTerminate() event and let the threads FreeOnTerminate afterwards. If I wanted to free
them myself, when should I do it?
The only strict rule is after they are finished executing. The choice would probably be guided by the design of the application. What would be different is, you wouldn't be able to use FreeOnTerminate and you would keep references to your threads to be able to free them. In the test case I worked on for answering this question, the worker threads which are finished are freed when a timer fires, kind of like a garbage collector.
Can I free a thread in its OnTerminate event or is this a tiny bit too early?
Freeing an object in one of its own event handlers induces a risk of operating on freed instance memory. The documentation specifically warns against this for components but in general this is applicable to all classes.
Even if you'd want to disregard the warning, this is a deadlock. Although the handler is called after Execute returns, OnTerminate is still synchronized from the ThreadProc. If you attempt to free the thread in the handler, it will cause a wait from the main thread for the thread to finish - which is waiting for the main thread to return from OnTerminate, which is a deadlock.
How would a thread inform me that it is done if not with OnTerminate?
OnTerminate is fine for informing that a thread has done its job, although you can use other means like using synchronization objects or queuing a procedure or posting a message etc.. Also worth noting that it's possible to wait on a thread handle, which is what TThread.WaitFor does.
In my test program I tried to determine application termination time depending on various exit strategies. All test results are dependent on my testing environment.
Termination time is measured starting from when the OnClose handler of a VCL form is called and ending with just before ExitProcess is called by the RTL. Also, this method does not account for how long ExitProcess takes, which I presume would be different when there are dangling threads. But I didn't try to measure it anyway.
Worker threads query the existence of a directory on a non-existing host. This is the most I could come up on waiting time. Every query is on a new non-existing host, otherwise DirectoryExists returns immediately.
A timer starts and collects worker threads. Depending on the time the IO query takes (which is around 550ms) the timer interval effects the total count of threads at any given time. I tested on around 10 threads with a timer interval of 250ms.
Various debug outputs allow to follow the flow in the event log of the IDE.
My first test was to leave the worker threads behind - just quit the application. The time I measured was 30-65ms. Again, this could have caused ExitProcess itself to take longer.
Next, I tested terminating the threads with TerminateThread. This took 140-160ms. I believe this is actually closer to what the previous test would come up if the time ExitProcess takes could be accounted for. But I have no proof on that.
Next, I tested cancelling the IO request on running threads and then leaving them behind.This considerably decreased the amount of leaked memory, in fact completely eliminated in most of the runs. Although the cancellation request is asynchronous, nearly all of the threads return immediately and find the time to finish. Anyway, this took 160-190ms.
I should note here that the code in DirectoryExists is defective, at least in XE2. The first thing the function does is to call GetFileAttributes. An INVALID_FILE_ATTRIBUTES return denotes the function failed. This is how the RTL handles the fail:
function DirectoryExists(const Directory: string; FollowLink: Boolean = True): Boolean;
...
...
Result := False;
Code := GetFileAttributes(PChar(Directory));
if Code <> INVALID_FILE_ATTRIBUTES then
begin
...
end
else
begin
LastError := GetLastError;
Result := (LastError <> ERROR_FILE_NOT_FOUND) and
(LastError <> ERROR_PATH_NOT_FOUND) and
(LastError <> ERROR_INVALID_NAME) and
(LastError <> ERROR_BAD_NETPATH);
end;
end;
This code assumes that unless GetLastError returns one of the above error codes the directory exists. This reasoning is flawed. Indeed, when you cancel the IO request, GetLastError returns ERROR_OPERATION_ABORTED (995) as documented but DirectoryExists returns true whether the directory exists or not.
Waiting for the threads to finish without cancelling IO takes 330-530ms. This completely eliminates memory leaks.
Cancelling IO requests and then waiting for the threads to finish takes 170-200ms. Of course no memory leaks here either. Considering there are no significant timing difference in any of the options, this would be the one I choose.
Testing code I used is below:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
Vcl.Controls, Vcl.Forms, Vcl.ExtCtrls,
generics.collections;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormDestroy(Sender: TObject);
private
FThreads: TList<TThread>;
end;
var
Form1: TForm1;
implementation
uses
diagnostics;
{$R *.dfm}
type
TIOThread = class(TThread)
private
FTarget: string;
protected
constructor Create(Directory: string);
procedure Execute; override;
public
destructor Destroy; override;
end;
constructor TIOThread.Create(Directory: string);
begin
FTarget := Directory;
inherited Create;
end;
destructor TIOThread.Destroy;
begin
inherited;
OutputDebugString(PChar(Format('Thread %d destroyed', [ThreadID])));
end;
procedure TIOThread.Execute;
var
Watch: TStopwatch;
begin
OutputDebugString(PChar(Format('Thread Id: %d executing', [ThreadID])));
Watch := TStopwatch.StartNew;
ReturnValue := Ord(DirectoryExists(FTarget));
Watch.Stop;
OutputDebugString(PChar(Format('Thread Id: %d elapsed time: %dms, return: %d',
[ThreadID, Watch.Elapsed.Milliseconds, ReturnValue])));
end;
//-----------------------
procedure TForm1.FormCreate(Sender: TObject);
begin
FThreads := TList<TThread>.Create;
Timer1.Interval := 250;
Timer1.Enabled := True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FThreads.Free;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
ShareName: array [0..12] of Char;
i: Integer;
H: THandle;
begin
for i := FThreads.Count - 1 downto 0 do
if FThreads[i].Finished then begin
FThreads[i].Free;
FThreads.Delete(i);
end;
for i := Low(ShareName) to High(ShareName) do
ShareName[i] := Chr(65 + Random(26));
FThreads.Add(TIOThread.Create(Format('\\%s\share', [string(ShareName)])));
OutputDebugString(PChar(Format('Possible thread count: %d', [FThreads.Count])));
end;
var
ExitWatch: TStopwatch;
// not declared in XE2
function CancelSynchronousIo(hThread: THandle): Bool; stdcall; external kernel32;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
i: Integer;
Handles: TArray<THandle>;
IOPending: Bool;
Ret: DWORD;
begin
ExitWatch := TStopwatch.StartNew;
// Exit;
Timer1.Enabled := False;
{
for i := 0 to FThreads.Count - 1 do
TerminateThread(FThreads[i].Handle, 0);
Exit;
//}
if FThreads.Count > 0 then begin
SetLength(Handles, FThreads.Count);
for i := 0 to FThreads.Count - 1 do
Handles[i] := FThreads[i].Handle;
//{
OutputDebugString(PChar(Format('Cancelling at most %d threads', [Length(Handles)])));
for i := 0 to Length(Handles) - 1 do
if GetThreadIOPendingFlag(Handles[i], IOPending) and IOPending then
CancelSynchronousIo(Handles[i]);
//}
//{
Assert(FThreads.Count <= MAXIMUM_WAIT_OBJECTS);
OutputDebugString(PChar(Format('Will wait on %d threads', [FThreads.Count])));
Ret := WaitForMultipleObjects(Length(Handles), #Handles[0], True, INFINITE);
case Ret of
WAIT_OBJECT_0: OutputDebugString('wait success');
WAIT_FAILED: OutputDebugString(PChar(SysErrorMessage(GetLastError)));
end;
//}
for i := 0 to FThreads.Count - 1 do
FThreads[i].Free;
end;
end;
procedure Exiting;
begin
ExitWatch.Stop;
OutputDebugString(PChar(
Format('Total exit time:%d', [ExitWatch.Elapsed.Milliseconds])));
end;
initialization
ReportMemoryLeaksOnShutdown := True;
ExitProcessProc := Exiting;
end.
New here. Relatively new to Delphi as well so plz be kind...
My actual (domain) problem: small VCL app that communicates with two laboratory balances via serial, balances output weight readings on a continuous 1-second interval, said weighs are displayed in the captions of two labels. When user clicks a 'Weigh' button, I need to wait for a valid weight (stable, within range, etc.) and record said weight once, -or-, allow the user to cancel the weighing.
My problem as implemented. Creating a separate thread when user clicks weigh button using TEvent template from S.MAHDI / David Heffernan as shown in this post.
TWeigh = class(TThread)
private
FTerminateEvent: TEvent;
protected
procedure Execute; override;
procedure TerminatedSet; override;
public
constructor Create(ACreateSuspended: Boolean);
destructor Destroy; override;
end;
constructor TWeigh.Create(ACreateSuspended: Boolean);
begin
FTerminateEvent := TEvent.Create(nil, True, False, '');
inherited;
end;
destructor TWeigh.Destroy;
begin
inherited;
FTerminateEvent.Free;
end;
procedure TWeigh.TerminatedSet;
begin
FTerminateEvent.SetEvent;
Beep;
end;
procedure TWeigh.Execute();
begin
while (not Terminated) do begin
if (validweight) then begin
Synchronize(procedure begin
DoStuff();
end);
end;
FTerminateEvent.WaitFor(100);
end;
end;
My form button click event looks like:
{ Weigh is global implementation var of class TWeigh }
procedure TForm1.btnWeighClick(Sender: TObject);
var
B : TButton;
begin
B := Sender as TButton;
if (B.Caption = 'Weigh') then Weigh := TWeigh.Create(False);
if (B.Caption = 'Cancel') then Weigh.Free;
B.Caption := Trim(Copy('CancelWeigh ',AnsiPos(B.Caption,'Weigh Cancel'),6));
end;
This appears to work fine for the cancellation requirement. It's my understanding that Synchronize messages the main thread and it is the main thread that executes the anonymous procedure containing DoStuff(), so there should be no race conditions between the user clicking cancel and a valid weight coming in. (Right?)
I'm stuck on how to have the weigh thread only execute a single time. Various solutions I've tried have resulted in deadlocks (add Weigh.Free to DoStuff()... didn't take long to figure out why THAT doesn't work), single execution but non-free'd threads (self.Terminate after the synchronize section within the if block), or various other nonsense.
So, is it even possible to have this thread free and/or kill itself while still allowing for the parent thread via user input to kill it, or do I need a completely different architecture for this?
Edit in response to why a loop: I only need a single reading, but the time until I get that single reading varies between immediately and never. The balances can take several seconds to stabilize, during which time unstable readings are read and displayed every second. The ability for the user to cancel is still required because the reading might never be valid (under-overweight).
If I understand correctly, you wish to quit the thread when you've finished calling DoStuff. That can be done like so:
procedure TWeigh.Execute();
begin
while (not Terminated) do begin
if (validweight) then begin
Synchronize(procedure begin
DoStuff();
end);
exit;
end;
FTerminateEvent.WaitFor(100);
end;
end;
I have to say that this looks more appropriate for a timer than a thread. All the work is done on the main thread, and the thread just appears to be there to check a flag at a regular interval. That sounds like a timer. In fact, why even a timer? Why not fire the DoStuff when you set the flag true?
I have a Delphi application which spawns 6 anonymous threads upon some TTimer.OnTimer event.
If I close the application from the X button in titlebar Access Violation at address $C0000005 is raised and FastMM reports leaked TAnonymousThread objects.
Which is the best way to free anonymous threads in Delphi created within OnTimer event with TThread.CreateAnonymousThread() method?
SOLUTION which worked for me:
Created a wrapper of the anonymous threads which terminates them upon being Free-ed.
type
TAnonumousThreadPool = class sealed(TObject)
strict private
FThreadList: TThreadList;
procedure TerminateRunningThreads;
procedure AnonumousThreadTerminate(Sender: TObject);
public
destructor Destroy; override; final;
procedure Start(const Procs: array of TProc);
end;
{ TAnonumousThreadPool }
procedure TAnonumousThreadPool.Start(const Procs: array of TProc);
var
T: TThread;
n: Integer;
begin
TerminateRunningThreads;
FThreadList := TThreadList.Create;
FThreadList.Duplicates := TDuplicates.dupError;
for n := Low(Procs) to High(Procs) do
begin
T := TThread.CreateAnonymousThread(Procs[n]);
TThread.NameThreadForDebugging(AnsiString('Test thread N:' + IntToStr(n) + ' TID:'), T.ThreadID);
T.OnTerminate := AnonumousThreadTerminate;
T.FreeOnTerminate := true;
FThreadList.LockList;
try
FThreadList.Add(T);
finally
FThreadList.UnlockList;
end;
T.Start;
end;
end;
procedure TAnonumousThreadPool.AnonumousThreadTerminate(Sender: TObject);
begin
FThreadList.LockList;
try
FThreadList.Remove((Sender as TThread));
finally
FThreadList.UnlockList;
end;
end;
procedure TAnonumousThreadPool.TerminateRunningThreads;
var
L: TList;
T: TThread;
begin
if not Assigned(FThreadList) then
Exit;
L := FThreadList.LockList;
try
while L.Count > 0 do
begin
T := TThread(L[0]);
T.OnTerminate := nil;
L.Remove(L[0]);
T.FreeOnTerminate := False;
T.Terminate;
T.Free;
end;
finally
FThreadList.UnlockList;
end;
FThreadList.Free;
end;
destructor TAnonumousThreadPool.Destroy;
begin
TerminateRunningThreads;
inherited;
end;
End here is how you can call it:
procedure TForm1.Button1Click(Sender: TObject);
begin
FAnonymousThreadPool.Start([ // array of procedures to execute
procedure{anonymous1}()
var
Http: THttpClient;
begin
Http := THttpClient.Create;
try
Http.CancelledCallback := function: Boolean
begin
Result := TThread.CurrentThread.CheckTerminated;
end;
Http.GetFile('http://mtgstudio.com/Screenshots/shot1.png', 'c:\1.jpg');
finally
Http.Free;
end;
end,
procedure{anonymous2}()
var
Http: THttpClient;
begin
Http := THttpClient.Create;
try
Http.CancelledCallback := function: Boolean
begin
Result := TThread.CurrentThread.CheckTerminated;
end;
Http.GetFile('http://mtgstudio.com/Screenshots/shot2.png', 'c:\2.jpg');
finally
Http.Free;
end;
end
]);
end;
No memory leaks, proper shutdown and easy to use.
If you want to maintain and exert control over a thread's lifetimes then it must have FreeOnTerminate set to False. Otherwise it is an error to refer to the thread after it has started executing. That's because once it starts executing, you've no ready way to know whether or not it has been freed.
The call to CreateAnonymousThread creates a thread with FreeOnTerminate set to True.
The thread is also marked as FreeOnTerminate, so you should not touch the returned instance after calling Start.
And so, but default, you are in no position to exert control over the thread's lifetime. However, you could set FreeOnTerminate to False immediately before calling Start. Like this:
MyThread := TThread.CreateAnonymousThread(MyProc);
MyThread.FreeOnTerminate := False;
MyThread.Start;
However, I'm not sure I would do that. The design of CreateAnonymousThread is that the thread is automatically freed upon termination. I think I personally would either follow the intended design, or derive my own TThread descendent.
To avoid errors using CreateAnonymousThread just set FreeOnTerminate to False before starting it.
This way you can work with the thread as you usually do without any workaround.
You can read the documentation that says that CreateAnonymousThread automatically sets FreeOnTerminate to True and this is what is causing the errors when you reference the thread.
Make your threads watch for some kind of notification from the outside. This could be an event that gets signaled, a message sent to a window owned by the thread, a command sent over a socket that your thread listens to, or whatever other form of communication you find.
If you determine that this problem is because your threads are so-called "anonymous" threads, then a simple workaround is for you to make them be non-anonymous threads. Put the body of the anonymous function into the Execute method, and pass any captured variables to the thread class via its constructor.
I just realized that my exceptions are not being shown to the user in my threads!
At first I used this in my thread for raising the exception, which does not work:
except on E:Exception do
begin
raise Exception.Create('Error: ' + E.Message);
end;
The IDE shows me the exceptions, but my app does not!
I have looked around for a solution, this is what I found:
Delphi thread exception mechanism
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22039681.html
And neither of these worked for me.
Here's my Thread unit:
unit uCheckForUpdateThread;
interface
uses
Windows, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
IdHTTP, GlobalFuncs, Classes, HtmlExtractor, SysUtils, Forms;
type
TUpdaterThread = class(TThread)
private
FileGrabber : THtmlExtractor;
HTTP : TIdHttp;
AppMajor,
AppMinor,
AppRelease : Integer;
UpdateText : string;
VersionStr : string;
ExceptionText : string;
FException: Exception;
procedure DoHandleException;
procedure SyncUpdateLbl;
procedure SyncFinalize;
public
constructor Create;
protected
procedure HandleException; virtual;
procedure Execute; override;
end;
implementation
uses
uMain;
{ TUpdaterThread }
constructor TUpdaterThread.Create;
begin
inherited Create(False);
end;
procedure TUpdaterThread.Execute;
begin
inherited;
FreeOnTerminate := True;
if Terminated then
Exit;
FileGrabber := THtmlExtractor.Create;
HTTP := TIdHTTP.Create(nil);
try
try
FileGrabber.Grab('http://jeffijoe.com/xSky/Updates/CheckForUpdates.php');
except on E: Exception do
begin
UpdateText := 'Error while updating xSky!';
ExceptionText := 'Error: Cannot find remote file! Please restart xSky and try again! Also, make sure you are connected to the Internet, and that your Firewall is not blocking xSky!';
HandleException;
end;
end;
try
AppMajor := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']'));
AppMinor := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']'));
AppRelease := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']'));
except on E:Exception do
begin
HandleException;
end;
end;
if (APP_VER_MAJOR < AppMajor) or (APP_VER_MINOR < AppMinor) or (APP_VER_RELEASE < AppRelease) then
begin
VersionStr := Format('%d.%d.%d', [AppMajor, AppMinor, AppRelease]);
UpdateText := 'Downloading Version ' + VersionStr;
Synchronize(SyncUpdateLbl);
end;
finally
FileGrabber.Free;
HTTP.Free;
end;
Synchronize(SyncFinalize);
end;
procedure TUpdaterThread.SyncFinalize;
begin
DoTransition(frmMain.TransSearcher3, frmMain.gbLogin, True, 500);
end;
procedure TUpdaterThread.SyncUpdateLbl;
begin
frmMain.lblCheckingForUpdates.Caption := UpdateText;
end;
procedure TUpdaterThread.HandleException;
begin
FException := Exception(ExceptObject);
try
Synchronize(DoHandleException);
finally
FException := nil;
end;
end;
procedure TUpdaterThread.DoHandleException;
begin
Application.ShowException(FException);
end;
end.
If you need more info just let me know.
Again: The IDE catches all the exceptions, but my program does not show them.
EDIT: It was Cosmin's solution that worked in the end - and the reason it didn't at first, was because I didn't add the ErrMsg variable, instead I just placed whatever the variable would contain into the Synchronize, which would NOT work, however I have NO idea why. I realized it when I had no other ideas, and I just messed around with the solutions.
As always, the joke's on me. =P
Something very important you need to understand about multi-theraded development:
Each thread has its own call-stack, almost as if they're separate programs. This includes the main-thread of your program.
Threads can only interact with each other in specific ways:
They can operate on shared data or objects. This can lead to concurrency issues 'race conditions', and therefore you need to be able to help them 'share data nicely'. Which brings us to the next point.
They can "signal each other" using a variety of OS support routines. These include things like:
Mutexes
Critical Sections
Events
And finally you can send messages to other threads. Provided the thread has in some way been written to be a message receiver.
NB: Note that threads cannot strictly speaking call other threads directly. If for example Thread A tried to call Thread B directly, that would be a step on Thread A's call-stack!
This brings us to the topic of the question: "exceptions are not being raised in my threads"
The reason for this is that all an exception does is:
Record the error
And unwind the call-stack. <-- NB: Your TThread instance can't unwind the main thread's call-stack, and cannot arbitrarily interrupt the main threads execution.
So TThread will not automatically report exceptions to your main application.
You have to make the explicit decision as to how you wish to handle errors in threads, and implement accordingly.
Solution
The first step is the same as within a single threaded application. You need to decide what the error means and how the thread should react.
Should the thread continue processing?
Should the thread abort?
Should the error be logged/reported?
Does the error need a user decision? <-- This is by far the most difficult to implement, so we'll skip it for now.
Once this has been decided, implement the appropriate excpetion handler.
TIP: Make sure the exception doesn't escape the thread. The OS won't like you if it does.
If you need the main program (thread) to report the error to the user, you have a few options.
If the thread was written to return a result object, then it's easy: Make a change so that it can return the error in that object if something went wrong.
Send a message to the main thread to report the error. Note, the main thread already implements a message loop, so your application will report the error as soon as it processes that message.
EDIT: Code Sample for indicated requirement.
If all you want to do is notify the user, then Cosmind Prund's answer
should work perfectly for Delphi 2010. Older versions of Delphi need a little more work. The following is conceptually similar to Jeff's own answer, but without the mistakes:
procedure TUpdaterThread.ShowException;
begin
MessageDlg(FExceptionMessage, mtError, [mbOk], 0);
end;
procedure TUpdaterThread.Execute;
begin
try
raise Exception.Create('Test Exception');
//The code for your thread goes here
//
//
except
//Based on your requirement, the except block should be the outer-most block of your code
on E: Exception do
begin
FExceptionMessage := 'Exception: '+E.ClassName+'. '+E.Message;
Synchronize(ShowException);
end;
end;
end;
Some important corrections on Jeff's own answer, including the implementation shown within his question:
The call to Terminate is only relevant if your thread is implemented within a while not Terminated do ... loop. Take a look at what the Terminate method actually does.
The call to Exit is an unnecessary waste, but you probably did this because of your next mistake.
In your question, you're wrapping each step in its own try...except to handle the exception. This is an absolute no-no! By doing this you pretend that even though an exception occurred, everything is ok. Your thread tries the next step, but is actually guaranteed to fail! This is not the way to handle exceptions!
Here's my very, very short "take" on the issue. It only works on Delphi 2010+ (because that version introduced Anonymous methods). Unlike the more sophisticated methods already posted mine only shows the error message, nothing more, nothing less.
procedure TErrThread.Execute;
var ErrMsg: string;
begin
try
raise Exception.Create('Demonstration purposes exception');
except on E:Exception do
begin
ErrMsg := E.ClassName + ' with message ' + E.Message;
// The following could be all written on a single line to be more copy-paste friendly
Synchronize(
procedure
begin
ShowMessage(ErrMsg);
end
);
end;
end;
end;
Threads don't automatically propagate exceptions into other threads. So you must deal with it yourself.
Rafael has outlined one approach, but there are alternatives. The solution Rafael points to deals with the exception synchronously by marshalling it into the main thread.
In one of my own uses of threading, a thread pool, the threads catch and take over the ownership of the exceptions. This allows the controlling thread to handle them as it pleases.
The code looks like this.
procedure TMyThread.Execute;
begin
Try
DoStuff;
Except
on Exception do begin
FExceptAddr := ExceptAddr;
FException := AcquireExceptionObject;
//FBugReport := GetBugReportCallStackEtcFromMadExceptOrSimilar.
end;
End;
end;
If the controlling thread elects to raise the exception it can do so like this:
raise Thread.FException at Thread.FExceptAddr;
Sometimes you may have code that cannot call Synchronize, e.g. some DLLs and this approach is useful.
Note that if you don't raise the exception that was captured, then it needs to be destroyed otherwise you have a memory leak.
Well,
It is gonna be hard without your source code, but i have tested this:
How to handle exceptions in TThread objects
And it works fine. Perhaps you should take a look at it.
EDIT:
You are not following what the links you point out tell us to do. Check my link and you will see how to do that.
EDIT 2:
Try that and tell me if it worked:
TUpdaterThread= class(TThread)
private
FException: Exception;
procedure DoHandleException;
protected
procedure Execute; override;
procedure HandleException; virtual;
end;
procedure TUpdaterThread.Execute;
begin
inherited;
FreeOnTerminate := True;
if Terminated then
Exit;
FileGrabber := THtmlExtractor.Create;
HTTP := TIdHTTP.Create(Nil);
try
Try
FileGrabber.Grab('http://jeffijoe.com/xSky/Updates/CheckForUpdates.php');
Except
HandleException;
End;
Try
AppMajor := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']'));
AppMinor := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']'));
AppRelease := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']'));
Except
HandleException;
End;
if (APP_VER_MAJOR < AppMajor) or (APP_VER_MINOR < AppMinor) or (APP_VER_RELEASE < AppRelease) then begin
VersionStr := Format('%d.%d.%d', [AppMajor, AppMinor, AppRelease]);
UpdateText := 'Downloading Version ' + VersionStr;
Synchronize(SyncUpdateLbl);
end;
finally
FileGrabber.Free;
HTTP.Free;
end;
Synchronize(SyncFinalize);
end;
procedure TUpdaterThread.HandleException;
begin
FException := Exception(ExceptObject);
try
Synchronize(DoHandleException);
finally
FException := nil;
end;
end;
procedure TMyThread.DoHandleException;
begin
Application.ShowException(FException);
end;
EDIT 3:
You said you are no able to catch EIdHTTPProtocolException. But it works for me. Try this sample and see it for yourself:
procedure TUpdaterThread.Execute;
begin
Try
raise EIdHTTPProtocolException.Create('test');
Except
HandleException;
End;
end;
I've previously used SendMessge for inter thread communication using the TWMCopyData, so I think the following should work:
Const MyAppThreadError = WM_APP + 1;
constructor TUpdaterThread.Create(ErrorRecieverHandle: THandle);
begin
Inherited Create(False);
FErrorRecieverHandle := Application.Handle;
end;
procedure TUpdaterThread.Execute;
var
cds: TWMCopyData;
begin
try
DoStuff;
except on E:Exception do
begin
cds.dwData := 0;
cds.cbData := Length(E.message) * SizeOf(Char);
cds.lpData := Pointer(#E.message[1]);
SendMessage(FErrorRecieverHandle, MyAppThreadError, LPARAM(#cds), 0);
end;
end;
end;
I've only used it for sending simple data types or strings, but I'm sure it could be adapted send more information through as necessary.
You'll need add Self.Handle to the constructor in form created the thread and Handle the messsage in the form which created it
procedure HandleUpdateError(var Message:TMessage); message MyAppThreadError;
var
StringValue: string;
CopyData : TWMCopyData;
begin
CopyData := TWMCopyData(Msg);
SetLength(StringValue, CopyData.CopyDataStruct.cbData div SizeOf(Char));
Move(CopyData.CopyDataStruct.lpData^, StringValue[1], CopyData.CopyDataStruct.cbData);
Message.Result := 0;
ShowMessage(StringValue);
end;
Strange that everyone answered this question but failed to spot the obvious problem: given that exceptions raised in a background thread are asynchronous, and can occur at any time, this means that showing exceptions from a background thread would pop-up a dialog box at random times to the user, quite possibly showing an exception that has nothing to do with what the user is doing at the moment. I doubt that doing this could possibly enhance the user experience.