Solved

Delphi 7: Threading

Posted on 2006-10-27
16
5,238 Views
Last Modified: 2008-01-09
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!
0
Comment
Question by:Marius0188
  • 9
  • 6
16 Comments
 

Author Comment

by:Marius0188
ID: 17818261
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?

0
 
LVL 2

Expert Comment

by:dominik-jesiolowski
ID: 17819455

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
0
 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 500 total points
ID: 17829539
I have used the Windows API for creating multi-threading applications in the past and actually prefer it in many cases since I don't have to create an additional class for my threads by just using the API solution.
However, don't use the CreateThread API but the Delphi BeginThread function! Why? Because BeginThread will make sure your application becomes aware of it's multiple threads.

However, working with threads can be very complex, depending on what you're trying to do. And multiple threads don't always bring you the expected speed gain or whatever else you're looking for. Even worse, working with multiple threads increase the risk of ending up with hard-to-find bugs in your code. Most programmers aren't able to think in a multithreading way but are more thinking in a linear way of processing information.

Now, what is the main problem with a multi-threading application? Well, there's basically only one simple issue, which happens to be access to any data in your application. For example, one thread might be reading a value while the other thread is writing to the same address. What happens when the read instruction gets interrupted by the write instruction is a bit unpredictable but in many situations, the information that is read will be invalid. There's no way of predicting when the system will switch from one thread to the next and it could well switch while it has read the first byte of a two-byte variable.

So, what solution should be used here? Well, first of all, you need to protect your data. Your resources, to be precise. There are several solutions for this but the most common one is the critical section. Sometimes a mutex might be helpful too. And you might want to delay execution of some code in one thread until another thread has finished, which you tend to use semaphores and events for. (Not Delphi events but WINAPI events.)

Now, about what you can and cannot call from a thread. Well, you can basically call everything in your thread but you must be aware of the data it modifies. Many VCL components have been made threadsafe by Borland  so there would not be much problems here. But ActiveX controls and TCP/IP communications tend to be strongly tied to a single thread. The same is also true for any Windows in your applications, which will only handle their messages from the thread that created the window, even though other threads might be sending messages to them.
Btw, about the VCL and threads... When the VCL gets notified that you're building a multithreaded application (by using the TThread class, BeginThread or setting IsMultiThread to true) all VCL code will slow down a bit because it starts protecting it's resources. Otherwise it will save a few clockcycles by avoiding this protection. Therefore, if you use CreateThread to create a new thread instead of BeginThread then the VCL doesn't know that it needs to protect it's resources, which makes calling VCL methods from your threads a bit more risky.

Exceptions in threads are handled as usual, with a try-except block. However, keep in mind that a thread that raises an exception will get killed silently and without any warning. It will just be gone. So in general my thread functions are always like this:

function MyThread(Parameter: Pointer): Integer;
begin
  try
    // Do whatever you want.
  except // Handle all possible exceptions
  end;
end;

How to report errors and other stuff from your thread? Well, there are just too many options for this, although you might send special messages to your mainform. Or you let your thread build a list of error messages and when the thread ends, the main thread could then pick up this messagelist and display it. (You can use WaitForSingleObject() on the thread handle with a timeout value of 0. If it returns a timeout error, the thread is still running so don't show any messages in that case.)
Of course, your thread can also display it's own messageboxes, although you should avoid creating Delphi forms from your child threads. (The messageloop of your application only runs from your main thread so there can be conflicts if you do this.)

And books? Well, ISBN 1-56592-296-4 or "WIN32 Multithreaded Programming" by O'Reilly publishers. It's the perfect book to use, even if it focuses on threads in C++. It doesn't even mention Delphi but that's not really needed if you're going to use the raw Windows API and want to learn more about how to use threads. Parts of it are quite valuable for Delphi developers while the rest is valuable in general.
0
 

Author Comment

by:Marius0188
ID: 18000697
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.
0
 

Author Comment

by:Marius0188
ID: 18001217
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!
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 18001710
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.
0
 

Author Comment

by:Marius0188
ID: 18003958
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.
:)
0
 

Author Comment

by:Marius0188
ID: 18004264
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
0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 18006375
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!
0
 

Author Comment

by:Marius0188
ID: 18006675
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?
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 18006839
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.
0
 

Author Comment

by:Marius0188
ID: 18006941
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.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 18007076
> 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.
0
 

Author Comment

by:Marius0188
ID: 18007109
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. :)


0
 

Author Comment

by:Marius0188
ID: 18007130
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.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 18008180
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.
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now