i want to check if Anonymous thread is running,i have an idea which is to monitor the thread status every 1s, if it Exists, restart the work again..
I've got the thread ID, now how to check the status ?
procedure TForm2.Button5Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure ()
var i : integer;
begin
inc(i);
label1.caption := TThread.Current.ThreadID.ToString;
end).Start;
end;
Threads do not just stop. If your thread stops functioning it is because your code has a defect. Even the simple code in the question contains two defects. It reads a local variable before it is initialized and it uses a VCL method away from the main thread.
The entire premise of your question is wrong. You don't need to monitor whether or not your thread is still running. You simply need to fix the defects in your code so that it does not fail.
A better understanding of what threads are and how to use them, will help you. A thread is usually a way to get something done without holding up the user-interface. If you want the user to wait for something to finish, don't use a thread, just put the work code in the buttonclick event handler without any thread creating.
You said
every 1s, if it Exists, restart the work again
That makes no sense. If the thread exists, it's still busy working so there's no need to restart it. Maybe you should tell us what work you want the thread to do exactly.
In the example below (taken from Background Operations on Delphi Android, with Threads and Timers, you can see that the Synchronize procedure is called when the work is done, so that's how you know that that thread is done with it's work.
procedure TForm2.Button5Click(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure ()
var i : integer;
begin
inc(i); // some work here
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
label1.Caption := TThread.Current.ThreadID.ToString;
end);
end).Start;
end;
TThread.CreateAnonymousThread is a function which returns an instance of new Thread like this:
var ms:TThread;
ms:=TThread.CreateAnonymousThread( .....
You have an instance - ms "aka Thread" and you can work with this object...
Related
My application continuously syncs data with another database and I am running this process in a thread of which I have very little knowledge.
I am pulling in data from an Actian Zen database. Formerly Pervasive.SQL and updating my database in NexusDB.
It is a continuous operation of refreshing data in NexusDB.
The thread is called from the main form.
The work is done here:
procedure tImportThread.Execute();
begin
while (not Terminated) do
Begin
TThread.Sleep(3000);
CheckPause;
//My work
end;
end;
At certain points in my application I want to pause and resume the thread.
I have read this post "Pausing" A Thread With A Property and the wiki it refers to.
I have implemented the solution offered in the above post for lack of other examples and lack of knowledge on my part. For the most part the thread is working fine except that I would like to pause and unpause the running at certain times and would like to see a log that it was actually paused or resumed.
What I want to know is:
Do I just use PAUSE and UNPAUSE in the parts of my application to suspend the thread and restart it?
I want to test with a button to Pause and Resume the thread but not sure how to implement:
If thread running then Pause else Unpause
Part of the solution offered includes this code:
procedure TMyThread.CheckPause;
var
SignaledEvent: THandleObject;
begin
while not Terminated do
begin
case TEvent.WaitForMultiple(FWaitEvents, INFINITE, False, SignaledEvent) of
wrSignaled: begin
if SignaledEvent = FRunEvent then Exit;
Break;
end;
wrIOCompletion: begin
// retry
end;
wrError: begin
RaiseLastOSError;
end;
end;
SysUtils.Abort;
end
Where to create an entry into a log file to show that the thread is now paused. I have a simple writelog function to create an entry but where in the above should I put it? The same when the thread is resumed.
Does it matter whether the thread is paused or running when the application terminate? Do I just use this code?
if Assigned(TMyThread) then
begin
TMyThread.Free;
end;
I have 10 threads working together. After starting the threads, 15 seconds later all threads exit before the job done, and only one thread remains.
My code:
procedure TForm1.Button2Click(Sender: TObject);
begin
AA;
BB;
CC;
DD;
EE;
FF;
GG;
HH;
II;
JJ;
end;
procedure TForm1.AA; //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ
begin
lHTTP := TIdHTTP.Create(nil);
TTask.Create(Procedure
try
//HTTP Opertations
finally
end;
end).Start;
end;
Note, i can't Free the HTTP component because if i did i get an AV and I don't know how to debug it, where to correctly free it in the code? However without freeing it the code works well but the threads exit. It might be the problem as Mr Dodge said.
Based on how I see you're creating the TIdHTTP component, it's simply wrong. You shouldn't create an object outside of the thread, then use it from inside the thread. That's not thread-safe. You should create it in the same thread as where it's being used. This is why you're unable to free it as well, so you actually have two problems to fix here at the same time.
I also realized that your lHTTP variable is not in the scope of your code, so I'm going to assume that you have it declared in some global (or otherwise shared) location. Each thread needs its own variable for its own instance.
So your code should look a little more like this:
procedure TForm1.AA; //same procedure for BB,CC,DD,EE.FF,JJ,HH,II,JJ
begin
TTask.Create(Procedure
var
lHTTP: TIdHTTP;
begin
lHTTP := TIdHTTP.Create(nil);
try
//HTTP Opertations
finally
lHTTP.Free;
end;
end).Start;
end;
Other components (such as TADOConnection) would even completely fail and crash for attempting such a thing (since such components utilize COM). Luckily, TIdHTTP does not use COM, but the design is still flawed for the same reason.
Now, when you say that you debugged it, I'm guessing you mean you debugged the code in the actual thread, but the breakpoint jumped to another place in your code before it reached the end of this? That is to be expected when using the debugger in threads. You can't just step into a thread and expect each sequential breakpoint to be in the same thread - I mean, if you have more than one breakpoint in different threads, your debugger is very likely to jump from one to another - because, again, they are multiple threads. I suggest creating some sort of work log, and each thread reports its status and position.
It is literally just like an alternate universe. Multiple different similar threads doing slightly different things than each other. The Delphi Debugger is simply the Time Lord who can see into all the alternate universes.
Right now have a multi-thread scheme like this:
//global variables
var
Form1: TForm1;
ControlFile: TextFile;
MaxThreads, iThreads: integer;
MyCritical: TCriticalSection;
The ControlFile is accessed by the threads, that do a ReadLn, and perform actions with the line obtained:
procedure TForm1.Button2Click(Sender: TObject);
var
HostLine: AnsiString;
FileHandle: integer;
begin
MyCritical:= TCriticalSection.Create;
MaxThreads:= 100;
iThreads:= 0;
while not(eof(ControlFile)) and (iThreads < MaxThreads) do
begin
inc(iThreads);
ReadLn(ControlFile, HostLine);
MyThread.Create(HostLine);
end;
end;
this block is the first doubt. I'm creating 100 threads, each one created received the current line of the textfile. But the problem is that on threads.onterminate, I execute this:
procedure MyThread.MainControl(Sender: TObject);
var
HostLine: string;
begin
try
MyCritical.Acquire;
dec(iThreads);
while not(eof(ControlFile)) and (iThreads < MaxThreads) do
begin
inc(iThreads);
ReadLn(ControlFile, HostLine);
MyThread.Create(HostLine);
end;
finally
MyCritical.Release;
end;
end;
The idea is to keep creating new threads, until the textfile finishes. But if one thread terminate, and execute this procedure, before the first while finished, what happens? The main thread, from button2click will be accessing the file, and the thread's procedure too. This looks strange to me. And the Critical Section, should be global or thread local? And this procedure, MainControl, that opens new threads until the end of the file, should be global or thread local?
First of all, I am not sure it's such a stellar idea to have different threads read from the same text file. It's not that it can't work, but I think it would be much cleaner to simply read the whole thing into a TStringList variable up front, which can then be shared among threads, if needed.
If you do go with what you already have, your critical section must be acquired in the main loop also - the threads that you spawn will start executing immediately by default, so it looks like there could be a race between your main thread and the threads that run MainControl, though you don't show exactly how that call is going to be made.
The critical section needs to be a global variable, as you have it, or a field/property of a global class in order to be shared among threads.
My final point is that it's probably not the greatest idea to create 100 threads either. Unless your threads are mostly waiting on I/O or events, you should generally not have more threads than you have CPU cores. It's better to use a pool of worker threads and a queue of work items that can then be doled out to the running threads. There's supposedly built-in support for some of this in more recent Delphi RTLs. I personally use my own tried and true custom thread pool implementation, so I can't give you any specific help with that part.
The call to OnTerminate is already synchronised.
procedure TThread.DoTerminate;
begin
if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;
So the critical section is not actually needed at all because all the code you've shown runs in the context of the main thread.
The app is written in Delphi XE.
I have two classes, a TBoss and TWorker, which are both based of of TThread.
The TBoss is a single instance thread, which starts up and then will create about 20 TWorker threads.
When the boss creates a instance of TWorker it assigns it a method to call synchronize on, when the Worker has finished with what it's doing it calls this method which allows the Boss to access a record on the Worker.
However I feel this is a problem, calling synchronize appears to be locking up the whole application - blocking the main (ui) thread. Really it should just be synchronizing that worker to the boss thread....
Previously I used messages/packed records to send content between threads which worked well. However doing it this way is much cleaner and nicer.... just very blocking.
Is there a way to call Syncronize in the worker to only wait for the Boss thread?
My code:
type
TWorker = class(TThread)
private
fResult : TResultRecord;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
....
end;
...
...
procedure TWorker.SendBossResults;
begin
if (Terminated = False) then
begin
Synchronize(SendResult);
end;
end;
procedure TWorker.SendResult;
begin
if (Terminated = false) and Assigned(FOnSendResult) then
begin
FOnSendResult(Self);
end;
end;
Then in my Boss thread I will do something like this
var
Worker : TWorker;
begin
Worker := TWorker.Create;
Worker.OnTerminate := OnWorkerThreadTerminate;
Worker.OnSendResult := ProcessWorkerResults;
So my boss then has a method called ProcessWorkerResults - this is what gets run on the Synchronize(SendResult); of the worker.
procedure TBoss.ProcessWorkerResults(Sender: TObject);
begin
if terminated = false then
begin
If TWorker(Sender).Result.HasRecord then
begin
fResults.Add(TWorker(Sender).Result.Items);
end;
end;
end;
Synchronize is specifically designed to execute code in the main thread; that's why it seems to lock everything up.
You can use several ways to communicate from the worker threads to the boss thread:
Add a callback to each worker thread,
and assign it from the boss thread
when it's created. It can pass back
whatever as parameters, along with a
thread ID or some other identifier.
Post a message from the worker thread
to the boss thread using
PostThreadMessage. The
disadvantage here is that the boss
thread has to have a window handle
(see Classes.AllocateHWnd in the
Delphi help and David Heffernan's comment below).
Use a good quality third-party
threading library. See
OmniThreadLibrary - it's free,
OS, and extremely well written.
My choice would be the third. Primoz has done all the hard work for you. :)
After your comment, here's something along the lines of my first suggestion. Note that this is untested, since writing the code for a TBoss and TWorker thread + a test app is a little long for the time I have right this minute... It should be enough to give you the gist, I hope.
type
TWorker = class(TThread)
private
fResult : TResultRecord;
fListIndex: Integer;
procedure SetOnSendResult(const Value: TNotifyEvent);
....
....
public
property OnSendResult: TNotifyEvent write SetOnSendResult;
property Result : TResultRecord read fResult;
property ListIndex: Integer read FListIndex write FListIndex;
....
end;
type
TBoss=class(TThread)
private
FWorkerList: TThreadList; // Create in TBoss.Create, free in TBoss.Free
...
end;
procedure TWorker.SendBossResults;
begin
if not Terminated then
SendResult;
end;
procedure TBoss.ProcessWorkerResults(Sender: TObject);
var
i: Integer;
begin
if not terminated then
begin
If TWorker(Sender).Result.HasRecord then
begin
FWorkerList.LockList;
try
i := TWorker(Sender).ListIndex;
// Update the appropriate record in the WorkerList
TResultRecord(FWorkerList[i]).Whatever...
finally
FWorkerList.UnlockList;
end;
end;
end;
end;
You could use a thread safe queue. In DelphiXE there is the TThreadedQueue. If you don't have DXE, try OmniThreadLibray - this library is very good for all threading issues.
As I mentioned new options in Delphi 2009 and higher, here is a link to an example for Producer / Consumer communication between threads, based on the new objct locks, in my blog:
Thread Synchronization with Guarded Blocks in Delphi
In a note regarding the deprecated methods TThread.Suspend and
TThread.Resume, The Embarcadero DocWiki for Delphi
recommends that “thread
synchronization techniques should be
based on SyncObjs.TEvent and
SyncObjs.TMutex.“ There is, however,
another synchronization class
available since Delphi 2009: TMonitor.
It uses the object lock which has been
introduced in this version ...
public properties of the TWorker class MUST have get and set methods, so you can use a Tcriticalsection to give the values of the properties. Otherwise, you´d be having thread-safe issues. Your example seems ok, but in the real world, with thousands of threads accessing to the same value would result in an read error. Use critical sections.. and you wouldn´t have to use any Synchronize. This way you avoid going to the message queues of windows and improve performance. Besides, if you use this code in a windows service app, (where windows messages aren´t allowed), this example wouldn´t work. The synchronize method doesn´t work unless there´s access to the windows message queue.
Solved!! (answer taken from the question)
The fixes made for this problem where two fold.
First remove the syncronization call in the TWorker SendBossResult method.
Second add a fProcessWorkerResult CritialSection to TBoss class. Create and Free this in create/destroy of the TBoss. In the ProcessWorkerResults method call fProcessWorkerResult.Enter and fProcessWorkerResult.leave around the code which needs to be safe from multiple worker results streaming in.
The above was the conclusion after Kens code and follow up comment. Many thanks kind sir, hats off to you!.
I have a main thread and a separate thread in my program. If the separate thread finishes before the main thread, it should free itself automatically. If the main thread finishes first, it should free the separate thread.
I know about FreeOnTerminate, and I've read that you have to be careful using it.
My question is, is the following code correct?
procedure TMyThread.Execute;
begin
... Do some processing
Synchronize(ThreadFinished);
if Terminated then exit;
FreeOnTerminate := true;
end;
procedure TMyThread.ThreadFinished;
begin
MainForm.MyThreadReady := true;
end;
procedure TMainForm.Create;
begin
MyThreadReady := false;
MyThread := TMyThread.Create(false);
end;
procedure TMainForm.Close;
begin
if not MyThreadReady then
begin
MyThread.Terminate;
MyThread.WaitFor;
MyThread.Free;
end;
end;
You can simplify this to:
procedure TMyThread.Execute;
begin
// ... Do some processing
end;
procedure TMainForm.Create;
begin
MyThread := TMyThread.Create(false);
end;
procedure TMainForm.Close;
begin
if Assigned(MyThread) then
MyThread.Terminate;
MyThread.Free;
end;
Explanation:
Either use FreeOnTerminate or free the thread manually, but never do both. The asynchronous nature of the thread execution means that you run a risk of not freeing the thread or (much worse) doing it twice. There is no risk in keeping the thread object around after it has finished the execution, and there is no risk in calling Terminate() on a thread that has already finished either.
There is no need to synchronize access to a boolean that is only written from one thread and read from another. In the worst case you get the wrong value, but due to the asynchronous execution that is a spurious effect anyway. Synchronization is only necessary for data that can not be read or written to atomically. And if you need to synchronize, don't use Synchronize() for it.
There is no need to have a variable similar to MyThreadReady, as you can use WaitForSingleObject() to interrogate the state of a thread. Pass MyThread.Handle as the first and 0 as the second parameter to it, and check whether the result is WAIT_OBJECT_0 - if so your thread has finished execution.
BTW: Don't use the OnClose event, use OnDestroy instead. The former isn't necessarily called, in which case your thread would maybe continue to run and keep your process alive.
Have the main thread assign a handler to the worker thread's OnTerminate event. If the worker thread finishes first, then the handler can signal the main thread to free the thread. If the main thread finishes first, it can terminate the worker thread. For example:
procedure TMyThread.Execute;
begin
... Do some processing ...
end;
procedure TMainForm.Create;
begin
MyThread := TMyThread.Create(True);
MyThread.OnTerminate := ThreadFinished;
MyThread.Resume; // or MyThread.Start; in D2010+
end;
const
APPWM_FREE_THREAD = WM_APP+1;
procedure TMainForm.ThreadFinished(Sender: TObject);
begin
PostMessage(Handle, APPWM_FREE_THREAD, 0, 0);
end;
procedure TMainForm.WndProc(var Message: TMessage);
begin
if Message.Msg = APPWM_FREE_THREAD then
StopWorkerThread
else
inherited;
end;
procedure TMainForm.StopWorkerThread;
begin
if MyThread <> nil then
begin
MyThread.Terminate;
MyThread.WaitFor;
FreeAndNil(MyThread);
end;
end;
procedure TMainForm.Close;
begin
StopWorkerThread;
end;
No, your code is not good (though it probably will work in 99.99% or even 100% cases). If you are planning to terminate work thread from main thread, don't set FreeOnTerminate to True (I don't see what are you trying to gain in the above code by setting FreeOnTerminate to True, it at least makes your code less understandable).
A more important situation with terminating work threads is that you are trying to close an application while work thread is in wait state. The thread will not be awaken if you just call Terminate, generally you should use additional syncronization object (usually event) to wake up the work thread.
And one more remark - there is no need for
begin
MyThread.Terminate;
MyThread.WaitFor;
MyThread.Free;
end;
if you look at TThread.Destroy code, it calls Terminate and WaitFor, so
MyThread.Free;
is enough (at least in Delphi 2009, have no Delphi 7 sources at hand to check).
Updated
Read mghie answer. Consider the following situation (better on 1 CPU system):
main thread is executing
procedure TMainForm.Close;
begin
if not MyThreadReady then
begin
MyThread.Terminate;
MyThread.WaitFor;
MyThread.Free;
end;
end;
it checked MyThreadReady value (it is False) and was switched off by scheduler.
Now scheduler switches to work thread; it executes
Synchronize(ThreadFinished);
and forces scheduler to switch back to main thread. Main thread continues execution:
MyThread.Terminate; // no problem
MyThread.WaitFor; // ???
MyThread.Free;
can you say what will happen at WaitFor? I can't (requires a deeper look into TThread sources to answer, but at first glance looks like a deadlock).
Your real error is something different - you have written an unreliable code and trying to find out is it correct or not. That is bad practice with threads - you should learn to write a reliable code instead.
As for resources - when the TThread (with FreeOnTerminate = False) is terminated the only resources that remains allocated is Windows thread handle (it does not use substantial Windows resources after thread is terminated) and Delphi TThread object in memory. Not a big cost to be on the safe side.
Honestly, your
... Do some processing
Is the real problem here. Is that a loop for doing something recursively? If not and, instead, thats a huge task, you should consider split this task in small procedures / functions, and put all together in the execute body, calling one after another with conditional if's to know the thread state, like:
While not Terminated do
begin
if MyThreadReady then
DoStepOneToTaskCompletion
else
clean_and_or_rollback(Something Initialized?);
if MyThreadReady then
DoStepTwoToTaskCompletion
else
clean_and_or_rollback(Something Initialized?, StepOne);
if MyThreadReady then
DoStepThreeToTaskCompletion
else
clean_and_or_rollback(Something Initialized?, StepOne, StepTwo);
Self.DoTerminate; // Not sure what to expect from that one
end;
It is dirty, almost a hack, but will work as expected.
About FreeOnTerminate, well... just remove the declaration and always
FreeAndNil(ThreadObject);
I'm not a fan of syncronise. I like more critical sections, for the flexibility to extend the code to handle more shared data.
On the form public section, declare:
ControlSection : TRTLCriticalSection;
On form create or somewhere else before thread.create ,
InitializeCriticalSection(ControlSection);
Then, every time you write to a shared resource (including your MyThreadReady variable), do
EnterCriticalSection ( ControlSection );
MyThreadReady := True; //or false, or whatever else
LeaveCriticalSection ( ControlSection );
Before you go (exit), call
DeleteCriticalSection ( ControlSection );
and free your thread as you always do.
Regards
Rafael
I would state that mixing models is simply not recommended. You either use FreeOnTerminate and never touch the thread again, or you don't. Otherwise, you need a protected way for the two to communicate.
Since you want fine control over the thread variable, then don't use FreeOnTerminate. If your thread finishes early, clear the local resources that the thread has consumed as you normally would, and then simply let the main thread free the child thread when the application is finished. You'll get the best of both worlds - resources freed by the child thread as soon as it can be, and no worries about thread synchronization. (And it's got the added bonus of being much simpler in design/code/understanding/support...)