Link to home
Start Free TrialLog in
Avatar of Marius0188
Marius0188

asked on

Delphi 7: Threading

Dear Experts,

I have a couple of threading related questions:


Scenario:
I have an email client application in development.
The Send and Receive process are coded in a Thread using Win API.
Example: "  ThreadHandle := CreateThread(nil, 0, @CheckNewMessagesThread, Pointer(AccountID), 0, ThreadID);"
When running this Thread and simaltaneously clicking on the MainForm's TreeView nodes the app hangs.
I know that my Thread might not be safe but I would like to get clarity on what I can AND what I CANT do.
On the Treeviews.OnChange event I make calls to Procedures that are also referenced in my Thread. (Problem, right?).

In my thread:
1. Can I make calls to other procedures and functions that the Application also uses?
    In the Thread I make a call to "Procedure TestProcedure()". How can I safely make a call to the very same
    Procedure from within my application and or Thread?

2. Regarding database.
   I am using TIBQuery and TIBTransactions in my application.
   The Procedure mentioned above uses a TIBQuery and TIBTransaction component?
   Is it safe? If not how to work around then?

3. Is there any advantage to create component dynamically (run time) used in Procedures that are been called from
    within the Thread and also the Main application? Does this have any advantage above creating components design
    time for example: WIth TIBQuery and TIBTransaction? And also any component(s) in general.

4. Creating a Thread using Win API, how can I safely update components on forms. For example: To show the progress
    of the thread I need to update a TLabel, TStatusBar.Panels[0].Text etc....  How to do this safely from within a Win API
    thread?

5. And some might suggest me to use Delphi's TThread class. Ok, but I would like to see if it is possible using Win API CreateThread(). If not possible at all the please explain all the above using TThread class.

BTW: Can anyone suggest a great book focus mainly on Delphi Threading from beginning to end.
         Willing to purchase if the books is really great and focusses on Threading.
         Free e-books and tutorials which are good will also be appreciated.


Please help as if I am total beginner.


Thanks!
Avatar of Marius0188
Marius0188

ASKER

Oh and I forgot to following important question(s):

6. How do I trap exception or errors in thread?

7. How do I report and display such erros? Using MessageDLG() ????

8. And then specifically, how can I update a TTreeview's nodes from within my thread?


I cannot help with pure API threads because I have never used them. TThread was created to simplify your development and I cannot see a reason for not using it (especially if you are a beginner).

1. From Delphi Help:
Do not use the properties and methods of other objects (my comment: VCL objects) directly in the Execute method of a thread.  Instead, separate the use of other objects into a separate procedure call, and call that procedure by passing it as a parameter to the Synchronize method.

2. You can use TIBQuery, etc. as long as they are not connected to DB-Aware VCL controls. But
remember to have a separate TIBConnection, that is not used by other thread (esp. VCL thread).

3. Hard to say in general.

4. I if you really have to use API look at the
TThread source. A simple solution: create a custom windows message handler in your form, from your thread send messages - they will be placed in VCL message queue and your message handler will be executed by VCL thread.

5. It is possible but I can't see any benefit. Delphi is object oriented and TThread is a object oriented way of dealing with threads.

6.

TMyThread = class(TThread)
private
  FE: Exception;
  procedure ShowException;
protected
  procedure Execute; override;
end;

implementation

procedure TMyThread.Execute;
begin
  try
    // code that may throw an exception
  except
    on E: Exception do
    begin
      FE := E;
      Synchronize(ShowException);
    end;
  end;
end;

procedure TMyThread.ShowException;
begin
  if Assigned(FE) then
    raise FE;
end;

if you don't have an exception handler in your Execute method you can still trap an exception in OnTerminate handler, property FatalException.

7. See example 6. You can use whatever you like
as long as it is in a procedure called using Synchronize().

8. See 7.

Regards
DJ
ASKER CERTIFIED SOLUTION
Avatar of Wim ten Brink
Wim ten Brink
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks to both of you.
Really good info.

One last thing.
I am not planning to implement multiple threads at the same time.

I only want to move the Send and Receive code of my application to Thread
to prevent the Application from hanging or becoming slow slow slow.

Will a thread help for this?

Any comments still welcome although I think a split of points would be just.
Oh and another last question.

When using the TThread class of Delphi.
MUST I put all my code in the Execute() procedure?

Or can I freely create procedures and function and just call them from the Excecute section?
This is mostly for split code into more readable pieces.

THanks!
No, you can use as many procedures and functions as you like, but it's the Execute procedure which determines in which order they are executed. And it's the Execute procedure that determines when your thread ends.
Also keep in mind that exceptions in your thread will kill your thread if your don't catch and handle them. In general, I use this code for the execute method:

procedure TMyThread.execute;
begin
  try
    DoSomeProcedure;
  except on E:Exception do HandleThisExceptionInSomeWay;
  end;
end;

Personally, I don't like the TThread class of Delphi that much but that's personal preference. I prefer to use BeginThread instead, which allows me to send a pointer value to my thread. This pointer is in general a pointer to my mainform and in my thread code I use this pointer to call a single method of my form. That way, my form is sharing it's variables with the thread so I don't have to pass all kinds of values to it. But it's not something I advise people to do who are a bit inexperienced with multi-threading issues. Using such a solution does require very good knowledge of how Windows is operating multiple threads. Also means you have to learn about the CreateEvent, CreateSemaphore, CreateMutex and of course critical sections. Perhaps even a bit of Windows messaging, knowing how to create a window in your thread so your thread can receive messages from other threads, etc. And of course learning how to synchronise between threads. It's very complex if you're inexperienced at this and you'll often discover that you might make many mistakes in this phase.
Worst case scenario? A thread eating up all CPU cycles because it's waiting for some other thread to finish it's actions. And of course the other thread is being starved from having any CPU time since some thread is eating up all CPU time. Been there, done that, learned from it yet I sometimes still make such mistakes. :-) It's easily prevented by using CreateEvent to create Windows events, though. But sometimes you forget about such "minor" details.
Thanks for all the help.

But in my current scenario I am only using TThread class to run a single background process.
No multiple threads will be created.

And as I go along I discover there is another question(s):

1. Can I have more than one "UpdateGuiProcedure" in Execute() ?
    For example:
   I have
          Procedure UpdateStatus();
          Procedure UpdateErrors();
   And both I call with Synchronize() in Execute()
   Because I only want to update the components relevant to errors when an error occurrs and also only status    
   when a certain progress has been made
   Example:
   Procedure TMyThread.Execute;
   begin
       //do some code
       Synchronize(UpdateStatus);
       //do some code
       Synchronize(UpdateErrors);
   end;



2. Can I only wrap the entire Execute() in 1 (one) Try Except?
    Or may I have multiple try and excepts there?
    For example:
   Procedure TMyThread.Execute;
   begin
       Try
           //do some code here
       Excecpt
       
      end;

      Try
            //do some code here
       Execpt
       
       end;
   end;

Hope this will be all for now.
:)
I have implemented one try execpt like this:
 Procedure Excecute;
 begin
  Try
    //Code HERE
  Except

  end;
 end;

In the Try section I have code which implements Indy components etc
but for some reason when the Indy components raise and exception
the Try Except wrapped around it does not catch it. As if Indy have their own
implementation. Can this be true of do I miss something somewhere?

In other words:
All exceptions raised by Indy Components (TidPop3) are not catched with the Try Except
You can do in threads everything you can do in your regular code. However, you have to keep in mind that if some resources (like variables, files ports) are shared between multiple threads then you have to make sure that no two threads will start trying to modify it at the same time! And yes, this can go as far as having to protect a single integer value with a critical section since the processor might do a context switch while it hasn't written the whole value in-memory yet! It could be that the first 2 bytes are therefore filled with the new value and the next two bytes still contain the old value.
Still, you might not notice these kinds of errors very quickly since the chance of two threads accessing the same resource at the same time tends to be a bit rare. Your code might be running for half a day without any problems and then crash without explanations simply because it took that long for such a conflict to arise.

So basically you just have to be very, very aware of all the resources that are in use by your application. And if in doubt, protect them with e.g. a critical section or by synchronising them.

Which reminds me of a funny error a collegue of mine once encountered. His application would sometimes crash when he closed it and he couldn't explain it. He used a critical section to protect his resources and yet a nasty error occurred. As it turned out, his main thread was releasing the critical section before the thread that also used the critical section was finished. :-)

So always make sure that you know when a thread ends!

About exceptions from third-party components. Some third-party components will create even more threads themselves and if an exception occurs, they synchronise this exception with the main thread instead of with the calling thread. But this kind of implementation tends to vary with every component. There are also some additional exception-handler components which seem to catch any exception within the application, thus overriding your basic exception handler.

And yes, you can have as many try-except blocks in your execute method as you like. Just as long as you remember that if an exception in your execute method occurs and it isn't caught by the except-part or some other exception handler then it will terminate your thread without any warning! It will just be gone!
Hi WorkShop Alex, I appreciated all your good advise and info.

1. I am correct to say.
If I only want to make sure one instance of the Thread should ran that
I can check the Thread handle? It will be = 0 when terminated?
Want to prevent the user from starting a new thread while current one is busy.

2. And in the my destructor I do free all TStrings etc that are created implicitly by me.
This is all I need to do. What the Thread created by itself it will also frees it by itself?
You can use GetExitCodeThread to get the exitcode of a thread. This function returns False if the thread hasn't finished yet.
Making sure you only create one thread is something you should do within your code. Many different solutions for that but the simplest one is by using just a single variable for the handle of your thread and initializing it with the value 0. Once you have assigned a thread handle to it, it will be non-zero meaning you can't create more threads.

If you want to do this cross-applications, allowing only one thread even if the user starts 5 instances of your application then you have to use a named Mutex and check for it to prevent more instances. But this is only required when you have to deal with multiple instances of your application.

You will have to terminate the thread yourself and if you use the TThread class, you do this by setting Terminated to true. However, this actually has no effect on your thread unless you check inside your execute functions if the Terminate property of the thread is set or not. (And if it's set, make sure your execute method jumps to the end as fast as possible.)
If the thread will free itself depends on how you set FreeOnTerminate to true or not. If you do set it to true, the thread will free itself. Otherwise your main source will have to check if the thread is finished and then free it.

Using threads can become quite complex, btw. And it is also hard to debug.
3. I dont have to call Free for my TMyThread after finished?
    It does that internally after Execute finished or when Terminated?
   So anything in the destructor will safely be executet?

4. I have actually defined component Properties in my TThread class which can be assigned when the Thread is created.
    For example: A ListView where all errors are reported to etc...
    So my thread creation looks like this iow (example):

   MyThread := TMyThread.Create(TRUE);
   MyThread.ListViewErrors := MainForm.ListView
   MyThread.Resume;

   I do check that the ListView is assigned before writing to it and I also do update it by calling through
   Synchronize(UpdateErrors);  
 
  Am I allow to do this?



And I dont know if I should post a new question but I need some help troubleshooting my Thread now.
I have build it to the best of my knowledge and I am using TIBQueries + TIBTransactions in my thread.
But exactly as you mentioned what happened with a colleague, I get AV error when closing my application.
I dont use critical sections.

And does it help to mention that I have only one instance of TMyThread running. Not multiple.
Nothing else can change the data but only the ONLY and CURRENT Thread.
> I dont have to call Free for my TMyThread after finished?

If you set FreeOnTerminate to true then yes, you would not have to free it in that case. However, it also means that when the thread quits then the object will be gone and asking for it's handle will result in an Exception or Access Violation.
Set it to False and your main thread will have to free it, after it is terminated.

Both options have their advantages. It's often just a matter of preferences.

> I have actually defined component Properties in my TThread class which can be assigned when the Thread is created.

Perfectly okay. Even more interesting, you can even access them when your thread is running! However, when your thread is running they become shared resources and you will need to use a critical section to protect access to those properties. (And yet also avoid the dreaded deadlock where your thread is trying to enter the same critical section twice.) It's okay to experiment with this all since the best way to learn to work with threads is by doing a lot of trial and error! :-)

> I have build it to the best of my knowledge and I am using TIBQueries + TIBTransactions in my thread.

Again, third-party components might be interacting with the main thread. It's not unusual for a component to contact it's parent and ask for information. If you create those components in runtime and use .Create(nil) to create those component then there will be less risks. The VCL is mostly threadsafe to use so don't expect too many problems at this level.

And AV's at the end of the application are common in multithreaded projects which aren't fully bugfree yet. Basically this means that you need more synchronisation between your main thread and child thread or more exception handlers to catch those unhandled errors and hide the AV's.
1. The Synchronize() method:

Must I always use this to call my procedures?
What are the conditions when to use Synchronize() ?
For example:
   1. Must I even call third party component's methods with Synchronize()?
   2. Should I only call my procedures that use VCL's and change variables with Synchronize()?

2. What is the flow of execution in the Execute() ?
    Does it execute logically from top to bottom and does it execute line by line.
    It wont jump to the next line while the previous line's Function has not returned a result yet (example)?
    Or is this what Synchronize should be used for. In other words ALWAYS and with EVERY single Procedure & Function
    one MUST use Synchronize to make calls, within then Execute()?

BTW: I realizes Threads can become complicated but sooner or later I need to get it under the knee.
         So I am willing to walk the mile. The advantages it can bring to an app I believe out numbers the disadvantages
         of complexity etc... There was no need for me to discover Threads in detail previously.
         But now it's a must.

I am going to award you with the total points. :)


Oh sorry and also in regards to the Synchronize method.
It can not call Procedures / Functions which takes parameters?

Obviously the work around is variables.
But is there any danger declaring local, global, private variables?
What are the conditions here?

Also keep in mind that with the current situation I am faced with there
is not need for multi-threading. Only 1 Thread will be running when the user decides to
do so. The thread can be started more than 1 time in applications lifetime but not at the same time.
Actually, you can create multithreaded applications without using the Synchronise method. You only need to use Synchronise if you need to execute code from within the main thread. But more commonly you could use a critical section to protect areas of your code. When you enter a critical section, you cannot enter it again from any thread. (Thus, calling it twice from your thread will lead to a deadlock.) Only when you leave a critical section again will another piece of code (in another thread) enter the critical section.

What in general happens with a multithreaded application is that two or more actions execute at exactly the same time. (If you have more than one CPU.) On a single CPU, they will just switch, spending X milliseconds on thread 1 then spending x seconds on thread 2. At the moment of the switch, the whole project is frozen including all it's read/write actions. Thus, if it's halfway writing an integer variable, it will just be filled halfway. If another thread then accesses the variable, it will read an invalid value.