Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I want to run my Thread then run application instruction. why run all after Sleep?
I have a TQuery(it has many record and slow fetch) instead Sleep and when Open, Thread no run before Open TQuery.
ShowMessage and Sleep are for test.
What's solution?
TCustomThread = class(TThread)
public
procedure Execute; override;
procedure doProc;
end;
.
.
.
procedure TCustomThread.Execute;
begin
Synchronize(doProc);
end;
procedure TCustomThread.doProc;
begin
ShowMessage('Thread');
end;
.
.
.
procedure TForm1.Button1Click(Sender: TObject);
var
thrd : TCustomThread;
begin
thrd := TCustomThread.Create(True);
thrd.Resume;
Sleep(3000);
ShowMessage('Form');
end;
I understand your question to be
Why does the form message appear before the thread message?
Simply put, your use of Synchonize means that all the code executes on the main thread. This means that the first message box must wait for the second message box to complete.
So, why does the form message box show first? The Synchronize method is used from threads to invoke code on the main thread. This Synchronize method signals the main thread that there is work to be done and then blocks until the main thread can do it.
The main thread won't start this work whilst it is busy in an event handler as your program is. So the call to Sleep actually blocks both threads because both threads are waiting on the main thread.
So to re-cap, the Synchronize method blocks until the main thread completes the work described in the procedure passed to Synchronize. If the main thread is busy then Synchronize blocks until the main thread completes its busy tasks.
Looking more broadly at your question you describe long running database tasks happening on the main thread. There is the fundamental problem. You must not have those tasks on the main thread. The main thread is dedicated to serving the GUI. That requires it to be responsive at all times and be able to service its message queue. Putting long running tasks on the main thread will destroy your GUI. Put those tasks on worker threads away from the main thread.
I suspect that you are using Synchronize because you've realised that the GUI is unresponsive because of your database code. And you reason that you can use a thread to keep the GUI responsive. But that can never work. The main thread must process messages in a timely manner. If the database code does not do so then you cannot service the message queue from the outside. There has to be co-operation.
The bottom line here is that long-running tasks must execute away from the main thread.
Button1Click is stopping the execution of the main thread for 3 seconds.
During the sleep(3000) execution, no GDI message is processed, so the dialog boxes are NOT displayed immediately.
In fact, TCustomThread.doProc works as expected, but the dialog box is not displayed until the GDI messages are processed.
You have to change this method e.g. into:
procedure TForm1.Button1Click(Sender: TObject);
var
thrd : TCustomThread;
expectedEnd: TDateTime;
begin
thrd := TCustomThread.Create(True);
thrd.Resume;
expectedEnd := Now+(1/24/60/60*3);
repeat
Sleep(50); // or any long process
Application.ProcessMessages; // to be called to process the GDI messages
until Now>=expectedEnd;
ShowMessage('Form');
end;
In short: never use Sleep() in the main GDI thread, for extended period of time (more than some ms), otherwise your application won't be responsive any more. And Windows will complain for it and ask you to kill the application!
So in your case, you have to call Application.ProcessMessages in your main thread when a long process is taken place (e.g. a TQuery), or run the query in a background thread (this is IMHO the preferred method), as we do e.g. for our Open Source mORMot framework on client side.
Related
Background
My Flagship App seems to leak memory. Well, not really but just during runtime. Investigating this showed the memory 'leak' is resolved when the app closes, but not in between.
I am using a background thread , that iself starts another thread. Both threads (parent and child) are 'Unobserved' which should result in the TOmniTaskControl object to be released and freed when the tread is finished.
Experimenting code
procedure TfrmMain.MyTestProc(const aTask: IOmniTask);
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
sleep(100);
end;
procedure TfrmMain.MyNestedTestProc(const aTask: IOmniTask);
var lTask:IOmniTaskControl;
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
lTask:=CreateTask(MyTestProc,'NestedTask');
lTask.Unobserved.Run;
sleep(100);
end;
procedure TfrmMain.btSimpleThreadClick(Sender: TObject);
var lTask:IOmniTaskControl;
begin
lTask:=CreateTask(MyTestProc,'SimpleThread');
lTask.Unobserved.Run;
end;
procedure TfrmMain.btNestedThreadClick(Sender: TObject);
var lTask:IOmniTaskControl;
begin
lTask:=CreateTask(MyNestedTestProc,'NestedThread');
lTask.Unobserved.Run;
end;
When debugging, and setting breakpoint on TOmniTaskControl.Destroy, and having a watch TOmniTaskControl.Name on I see the following:
btSimpleThreadCLick:
TOmniTaskControl for 'SimpleThread' gets to be created
TOmniTaskControl for 'SimpleThread' gets to be destroyed
btNestedThreadCLick:
TOmniTaskControl for 'NestedThread' is Created
TOmniTaskControl for 'NestedTask' is Created
TOmniTaskControl for 'NestedThread' is Destroyed
Problem: TOmniTaskControl for 'NestedTask' is NOT destroyed. Another issue is that the OnTerminate isn't called either.
Then, when closing the app, the TOmniTaskCOntrol for 'SimpleThread' is destroyed. (And also, the OnTherminate is fired)
Workaround
I came up with this solution which seems to do the trick. The thing is however, I do usually NOT run my subthreads from a form where a TOmniEventMonitor is at hand. So I'd have to create a global TOmniEventMonitor object for this.
But isn't this the whole point of the UnObserved method?
procedure TfrmMain.MyNestedTestProc(const aTask: IOmniTask);
var lTask:IOmniTaskControl;
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
lTask:=CreateTask(MyTestProc,'NestedTask');
lTask.MonitorWith(OmniEventMonitor).Run; // OmniEventMonitor is a component on my form
sleep(100);
end;
Well, secondary workarounbd... kind-of. It does not allow for my thread to be freed unattended.
if I change the NestedTestProc to the code below, then the NestedTaskgets to be destroyed at the expected moment. Unfortunately, this solution is clearly not 'Unobserved'
procedure TfrmMain.MyNestedTestProc(const aTask: IOmniTask);
var lTask:IOmniTaskControl;
begin
sleep(100);
SGOutputDebugStringFmt('%s.MyTestProc for %s',[ClassName,aTask.Name]);
lTask:=CreateTask(MyTestProc,'NestedTask');
try
lTask.Run;
lTask.WaitFor(2000);
finally
lTask:=nil;
end;
sleep(100);
end;
Update 20201029 >>
An invalid handle (1400) error typically occurs when the master task was completed and its associated Monitor was already destroyed.
So - The 'Master' task thread should not die in case there is an "owned" monitor that is monitoring other threads.
So to check this, I changed the timing (using sleep()) to ensure the child task was completed before the master task is completed.
The Invalid handle error is gone now, and the COmniTaskMsg_Terminated message gets to be posted successfully.
But still the ComniTaskMsg_Terminated from the child task is not processed. (I expected the thread of the MasterTask to handle this.)
IMO there are 2 problems:
life time management of the Unobserved monitor
shutdown management of the Unobserved monitor, which should keep the "owning" thread alive and keep processing messages until all
monitored threads/tasks are gone.
Also I wonder whether these shutdownmessages hould be handled/processed by the Application main thread (It seems this is the case now) or otherwise through a separate thread that checks all the monitors in GTaskControlEventMonitorPool . AIA, pretty complicated stuff :s...
Giving this some thought, monitors that were created by the application main thread (thus Monitor.ThreadID=MainThreadID) should be handle their messages in the main thread message loop, and all others probably need to be handled by a separate thread... Its just too confusing! I will see if I write a unit test for this, just to demonstrate what I expect to happen.
<< Update 20201029
The question
With OmniThreadLibrary, How can I use unobserved threads inside threads, and avoid the described memory leak?
not used TTask before and its a simple thing im trying to do.
While the main form executes a procedure (DoAnalyse) id like to display the TActivityIndicator on my main form without it slowing as the thread does the work. I used to use a progress bar but would prefer to use the more modern Activity Indicator.
I Have tried it two ways:
A simple:
Ttask.Run(DoAnalyse);
and
Task := TTask.Create( procedure
begin
DoAnalyse;
end);
Task.Start;
it executes perfectly and my activity indicator behaves smoothly.
Problem is when the procedure is finished the main form is locked and doesn't respond at all. im guessing I have to put some kind of call back to the main thread but cant find out how to do it.
Any ideas?
TTask doesn't have an event when it stops running. Your task procedure needs to use TThread.Synchronize() or TThread.Queue() (or any other inter-thread mechanism of your choosing) to notify the main thread before it exits.
Otherwise, use TThread instead of TTask. TThread has an OnTerminate event.
I have the following scenario:
The program I am writing simulates multiple air networks. Now I have written it so that each simulation is in its own thread with its own data. This is so that there can be no contamination of data and each thread is complete multitasking safe. I have implemented delphi's native TThread class for each thread.
The program has one main thread. It then creates multiple sub threads for each network (my current test case has 6). But each sub thread also has 4 additional threads since its network has more than one layout. So in total I have 31 threads, but only 24 threads actively processing. All threads are created in suspended mode so that I can manually start them.
The computer I am running on is an i5 laptop so it has 4 threads (2 physical cores + 2 HT). In the production environment this will run on servers so they will have more processing power.
Now in the main program thread I have added a break point after the for loop which executes the threads. This doesn't trigger immediately. Watching the debug console of Delphi, it looks like it is only actively running 4 threads at a time. As soon as one exits, it starts another thread.
The simulations in the threads are difficult to optimize and require a few loops to finish the simulation. And each loop requires the previous loop's data. But each simulation is completely independent of the next so the program is an excellent candidate for threads and pipe lining.
Now my question is why does it only start 4 threads and not all the threads as the code specifies?
EDIT Added pieces of code
Main program
for i := 0 to length(Solutions)-1 do
begin
Solutions[i].CreateWorkers; //Setup threads
end;
for i := 0 to length(Solutions)-1 do
begin
Solutions[i].Execute; //start threads
end;
end;
isSolversBusy := false; //breaking point doesn't trigger here
1St level of threads
procedure cSolution.Execute;
var
i : integer;
lIsWorkerStillBusy : boolean;
begin
lIsWorkerStillBusy := true;
for i := 0 to length(Workers)-1 do
begin
Workers[i].Start;
end;
while (lIsWorkerStillBusy) do
begin
lIsWorkerStillBusy := false;
for i := 0 to length(Workers)-1 do
begin
if Workers[i].IsCalculated = false then
begin
lIsWorkerStillBusy := true;
end;
end;
sleep(100);
end;
FindBestNetwork;
IsAllWorkersDone := true;
end;
2nd level of threads
procedure cWorker.Execute;
begin
IsCalculated := false;
Network.UpdateFlows;
Network.SolveNetWork; //main simulation work
CalculateTotalPower;
IsCalculated := true;
end;
EDIT 2
The reason I create them all suspended is because I store them in an array and before I start them I first have create the workers and their properties. I am simulating air network scenarios. Each solution is a different layout, while each worker is a different way of running that layout.
I have to first calculate all the worker's start properties before I start them all. In hind sight I could modify the code to do that in the thread. It currently happens in the main thread. The creation of the threads happens before the piece of code I pasted here.
The reason I keep them all in threads is that I need to evaluate the results of each thread afterwords.
This is your problem :
for i := 0 to length(Solutions)-1 do
begin
Solutions[i].Execute; //start threads
end;
This does not start the threads - this is executing the Execute method in the calling thread. In fact you were not running only 4 threads at a time, you were not even running one - all of this work would be done on the main thread sequentially. To resume a suspended thread you must use Solutions[i].Start.
The Execute method of a TThread is a special method that is executed on the worker thread created by the TThread automatically. When you create a TThread this method is automatically run on the worker thread that the TThread creates. If you create the thread suspended then it simply waits for you to wake the thread before beginning this work. Calling the .Start method of a TThread is what accomplishes this - triggering the underlying worker thread to begin executing the .Execute method.
Otherwise, the Execute method, and all other methods of a TThread are no different from any other normal method belonging to a class. They can be executed on any thread that calls them directly.
In this case, it doesn't seem like you are doing any additional work in the main thread between creating and executing your workers. In this case, unless you have an explicit need for it, you could simply create your threads not-suspended and let them execute automatically upon creation.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I run a multithread application and I want to limit the number of threads on my machine
The code concept goes currently like this (it is just a drft to show the major ideas behind)
// a List with all the threads I have
class MyTHreadList = List<TMyCalcThread>;
// a pool class, check how many threads are active
// try to start additions threads once the nr. of running THreads
// is below the max_thread_value_running
class MyTheardPool = Class
ThreadList : MyTHreadList;
StillRunningTHreads : Integer;
StartFromThreadID : Integer;
end;
var aTHreadList : MyTHreadList;
procedure MainForm.CallCreateThreadFunction( < THREAD PARAMS > );
begin
// just create but do not start here
MyTHread := TMyCalcThread.create ( < THREAD PARAMS > );
MyTHread.Onterminate := What_To_do;
// add all threads to a list
aTHreadList.add(MyTHread);
end;
/// (A)
procedure MainForm.What_To_do ()
var start : Integer;
begin
max_thread_value_running := max_thread_value_running -1;
if max_thread_value_running < max_thread_count then
begin
start := max_thread_count - max_thread_value_running;
startThereads(start,StartFromThreadID)
end;
end;
procedure MainForm.startThereads (HowMany , FromIndex : Integer);
var i : INteger;
begin
for I := FromIndex to HowMany + FromIndexdo
begin
// thread[i].start
end;
end;
MainForm.Button ();
/// main VCL form , create threats
for i := 0 to allTHreads do
begin
CallCreateTHreadFunction( < THREAD PARAMS > );
end;
......
/// (B)
while StillRunningTHreads > 0 do
begin
Application.processmessages;
end;
The complete idea is a small list with the Threads, on every individual Thread terminate step I update the number of running threads and start the now possible max. number of new threads.(A) Instead of a Function WaitforSingleObject () .... I do a loop at the end to wait for all threads to finish execution. (B)
From the code design I did not find any full example on the net, I may approach a vaild design or will I run into some trouble which I did not consider right now.
Any comment on the diesign or a better class design is welcome.
Don't try to micro-manage threads like this. Just don't. Either use the Win API threadpool calls, or make your own threadpool from a producer-consumer queue and TThread instances.
When a task is completed, I suggest that the work thread call an 'OnCompletion' TNotifyEvent of the task class with the task as the parameter. Ths can be set by the issuer of the task to anything they might wish, eg. postMessaging the task instance to the GUI thread for display.
Micro-managing threads, continually creating/waiting/terminating/destroying, Application.processmessages loops etc. is just horrible and almost sure to go wrong at some point.
Waiting for ANYTHING in a GUI event-handler is just bad. The GUI system is a state-machine and you should not wait inside it. If you want to issue a task from the GUI thread, do so but don't wait for it - fire it off and forget it until it is completed and gets posted back to a message-handler procedure.
Basically thread-pools are a nice feature (There is an implementation in the Win32 API, however I don't have any experience with it).
There is one basic stumble stone however: You need to remember that a task may be delayed until an empty thread is available. If you need synchronization between different tasks (e.g. tasks are waiting on other tasks) then you have a serious deadlock problem:
Just assume that all running tasks wait for a single task which is waiting for a free thread...
A similar problem can also happen if your threads wait for the main thread to react while the main thread waits for a new task to start.
If your tasks don't need any further synchronization (e.g. once a task is finished it will just mark itself as finished and the main thread will then later on read the result) you don't need to worry about this.
As a small side note:
I would use two separate lists: One for free (suspended) threads and one for running threads.
I'd consider to use an existing Thread-Pool implementation (like Winapi CreateThreadpool) before I created my own...
The loop (B) takes away a lot of CPU power from the threads. Don't wait actively in a loop for threads, use one of the WaitFor.... functions.
Reuse your threads and have a list of "todos" that the threads will execute. Thread creation and destruction can be expensive.
Apart from that I'd recommend that you use an existing library instead of reinventing the wheel. Or use at least the Windows API thread pool functions.
Our program creates a background thread at the beginning of the program. The background thread does some database integrity checks and checks for stuff in the Internet using Indy. After 10 seconds, the background thread should be finished and since FreeOnTerminate is true, it will also clean itself up.
We have noticed that in some cases, if the user closes the program too quickly, the process will still be alive until the background thread is finished.
Since we couldn't exactly reproduce the issue, I have created a demo project to try a few things:
type
TBackgroundThread = class(TThread)
protected
procedure Execute; override;
end;
{ TForm1 }
var
bt: TBackgroundThread;
procedure TForm1.FormCreate(Sender: TObject);
var
i: integer;
begin
// Create a background thread which runs X seconds and then terminates itself.
bt := TBackgroundThread.Create(false);
bt.FreeOnTerminate := true;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
// The user closes the app while the background thread is still active
Sleep(2000);
Close;
end;
{ TBackgroundThread }
procedure TBackgroundThread.Execute;
var
i: integer;
x: cardinal;
begin
inherited;
// Simulate some work that the background thread does
x := MaxInt;
for i := 0 to MaxInt do
begin
x := Random(x);
end;
end;
The result is a bit surprising to me: After I close the MainForm, the process will be immediately terminated and the background thread will get hard-killed.
Now I have a few questions about this:
After the closing of the MainForm (= exit of the main thread), should I manually terminate all created threads via .Terminate or will that be done automatically?
Shall my threads only check for Self.Terminated or should they also check for Application.Terminated ?
Why does my busy thread as shown above gets immediately killed when I close the application? I expected that the process Project1.exe will run until all threads have finished by themselfes. (And as described above, we had seen an application where the main form is closed, but a thread is preventing the process of being closed).
How is it possible then, that our real application's process does not terminate because of a running background thread? Might it have something to do with the Internet stuff, which might cause the app to wait until a connection timeout is reached?
Closing the main form is not synonymous with exiting the main thread. Code continues to run after the form is closed. In particular, units are finalized.
If you handle your test thread's OnTerminate event, or put a breakpoint in the Terminate method, you'll see that it's not called automatically when your program exits. You'll have to call it yourself. But note also that a thread doesn't stop running just because Terminate is called. It continues running until it stops itself or it's terminated forcefully. Call WaitFor to wait for it to terminate.
Don't bother checking Application.Terminated; the thread's property should be sufficient.
Your thread gets terminated forcefully as your program exits because eventually your program calls ExitProcess, and one of the things the OS does there is to terminate all other threads. It doesn't call Terminate on them because the OS doesn't know about Delphi classes and methods.
You'll have to do some more debugging to determine why your program doesn't terminate promptly for your customers. You say you can't reproduce the problem in house, and you've written a test program that doesn't exhibit the problem, either. You'll have to find a customer who will cooperate with your further debugging efforts. Do you really know it's the thread that's holding things up, or is that just a guess so far?