• Recent Tutorials

  • Tripping The Class Fantastic: Multi-threading Part 1

    Creating and Terminating Threads

    Now we've covered the background, lets look at some code. First off, the basics of a thread.

    Code:
    interface
    
    uses
      classes;
    
    type
      TVerySimpleThread = class(TThread)
      protected
        fCounter : integer;
      public
        procedure execute; override;
      end;
    
    implementation
    
    procedure TVerySimpleThread.execute;
    begin
      while not self.terminated do
        fCounter:=fCounter+1;
    end;
    The most important part of any thread class is the 'execute' method as this contains the core code your thread will execute when its running. In this simple example all we want our thread to do is constantly increment the counter (fCounter) while its running. Thats where the 'while' comes in. Without it, our thread would run, increment the counter once and then terminate when we exit the 'execute' method (this action may be what you want, and does allow you to create 'fire and forget' threads for handling one off operations in the background), but with the 'while', once the thread starts it will sit there incrementing our counter until it is explicitly terminated by calling the 'terminate' method.

    But what happens next?

    Well that depends on how you want to handle things. You have several different options relating to terminating and cleaning up your thread and your choice will be governed by how you are using the thread object. The options you have are 'fire and forget', 'application notification', 'terminate on demand'.

    Fire and Forget

    To implement a fire and forget thread, then you need to consider the 'freeOnTerminate' property, but you also need to ensure that your application either does not need to access these spawned threads or if it does, that it can cope with potentially not being able to gain access to the thread object (it may have terminated and been freed). Once your thread has done its job, it can end by leaving the 'execute' method.

    Code:
      // The nice way to create a 'fire and forget' thread (use a temporary variable)
      myThread:=TMyThreadClass.create(false);
      myThread.freeOnTerminate:=true;
    
      // And the dirty (true fire and forget) way...
      TMyThread.create(false).freeOnTerminate:=true;
    Of course you could also handle this by setting 'freeOnTerminate' in the constructor of your thread class. By default, 'freeOnTerminate' is false, so if you want to have the thread free itself, then make sure you set this flag.

    Application Notification


    If you want the thread to let your main application to know its terminated, you can provide a handler for the 'OnTerminated' event. This is a standard TNotify event (procedure(sender:TObject) of object). When the thread terminates, this handler is called and you can take any action you deem appropriate (including freeing up the thread object). But, bear in mind that if you've already set the 'freeOnTerminate' property to true and you free the thread in the OnTerminate event handler, you will be faced with an access violation as the system attempts to free the thread itself when it returns from the event handler.

    Code:
      ...
    
      myThread.onTerminate:=form1.myOnTerminateHandler;
    
      ...
    
    procedure TForm1.myOnTerminateHandler(sender:TObject);
    begin
      // Sender points to the TThread descendant that is ending
      sender.free;
    end;
    As a side note, its no good changing your mind about whether to have the thread free itself when you are in the 'onTerminate' event handler. By the time your event handling code is executed, the thread has already looked at the 'freeOnTerminate' property and stored its value in a variable (this is just in case you free it in this event handler... the code doesn't cause an AV when its deciding whether to free the thread or not). So, if you want to have a thread free itself, you must decide this and setup 'freeOnTerminate' BEFORE the thread terminates.

    Terminate on Demand

    The final key choice you have for handling the termination of a thread is what I would call 'Terminate on Demand'. This method of terminating a thread does not rely on using the 'freeOnTerminate' property or the 'onTerminate' event handler as you explicitly terminate the thread and then wait for it to finish. You can use 'freeOnTerminate' or the 'onTerminate' event handler to free up the thread, but if you are explicitly requesting the termination of the thread, its just as easy to free it up yourself when its completely finished.

    Code:
      ...
      // Create our thread and grab it
      myThread:=TMyThread.create(false);
      ...
    
      ...
      // Terminate our thread
      myThread.terminate;
    
      // Wait for it to finish up
      myThread.waitFor;
    
      // And free it
      myThread.free;
      ...
    Where possible, I would advise that you keep careful track of your threads in order to ensure that they are all terminated (you're application could hang on shutdown if a thread is still running or is sitting there suspended) and that any resources are cleaned up. This is especially important if you are writing a multithreaded service that spawns lots of threads.

    Comments 6 Comments
    1. chronozphere's Avatar
      chronozphere -
      Fantastic articles athena. I will definitely read 'em all.

      Multithreading still scares me a bit and the only way to overcome that is by rolling up the sleeves haha... I would like to use them in my engine, to separate rendering and audio from the rest of the code.

      I really need to write an article too btw. There aren't that many and new site does a good job at presenting them.
    1. WILL's Avatar
      WILL -
      Quote Originally Posted by chronozphere View Post
      I really need to write an article too btw. There aren't that many and new site does a good job at presenting them.
      Feel free! We need a lot more beginner articles. Either an introduction to a specific API or a completely non-API specific article about the basics of game programming.
    1. AthenaOfDelphi's Avatar
      AthenaOfDelphi -
      Thanks

      It's not quite as scary as it may seem at first... although I speak from the point of view of someone who nearly got fired because the first time they used multi-threading was in a business critical application which kept crashing because I messed up

      After that, no programming problem has ever seemed quite so scary
    1. code_glitch's Avatar
      code_glitch -
      nice stuff. i have written one slightly less popular article as it would seem but am working on another as will would tell you with the newbie-frindly project or something
    1. Dan's Avatar
      Dan -
      AthenaOfDelphi,
      Do you have any comments on suspend and resume procedures being deprecated starting from D2009. Since you are using these methods in your examples it may confuse some people because stuff normally gets deprecated when better ways of doing things become available.
    1. AthenaOfDelphi's Avatar
      AthenaOfDelphi -
      Dan, apologies for the delay... well, what can I say... I didn't realise they were deprecated, certainly my compiler doesn't complain about anything (I get the feeling from reading one or two articles it should complain).

      Whilst I understand the theories I've read, I can honestly say I've (a) never used the suspend/resume mechanism for synchronising threads and (b) never had any problems using suspend/resume. I generally use it for job processing type scenarios where the thread put's itself to sleep until it is needed. Replacing these calls with an eventing mechanisms (wait_for_single_object IIRC) shouldn't be too much of a problem. Something like this:-

      Code:
      unit classSuspendableThread;
      
      interface
      
      uses
        sysUtils, classes, windows;
      
      type
        TSuspendableThread = class(TThread)
        protected
          fWakeUpEvent        : THandle;
          fSleeping           : boolean;
      
          procedure goToSleep;
        public
          constructor create(createSuspended:boolean);
          destructor Destroy; override;
      
          procedure terminate;
      
          procedure wakeUp;
      
          property sleeping:boolean read fSleeping;
        end;
      
      implementation
      
      constructor TSuspendableThread.create(createSuspended:boolean);
      var
        temp    : string;
        nameBuf : PWideChar;
      begin
        inherited create(createSuspended);
      
        temp:='WUE-'+intToHex(self.ThreadId,8);
        nameBuf:=strAlloc(length(temp));
        strPCopy(nameBuf,temp);
        fWakeupEvent:=createEvent(nil,false,false,nameBuf);
        strDispose(nameBuf);
      end;
      
      destructor TSuspendableThread.destroy;
      begin
        closeHandle(fWakeupEvent);
      
        inherited;
      end;
      
      procedure TSuspendableThread.terminate;
      begin
        TThread(self).terminate;
      
        if (fSleeping) then
        begin
          wakeup;
        end;
      end;
      
      procedure TSuspendableThread.wakeUp;
      begin
        if (fSleeping) then
        begin
          try
            setEvent(fWakeupEvent);
          except
          end;
        end;
      end;
      
      procedure TSuspendableThread.goToSleep;
      begin
        fSleeping:=true;
        waitForSingleObject(fWakeupEvent,INFINITE);
        fSleeping:=false;
      end;
      
      end.
      This class works, call 'wakeUp' instead of resume (although resume, or it's replacement start would still be needed if you created it suspended). You will of course note that the 'gotoSleep' method which replaces suspend is now protected. Calling it from anywhere other than the execute method will hold the calling thread.

      This solution is far from perfect. The Windows API calls keep a count of suspends and resumes which this class does not. I suspect there are numerous ways of solving the puzzle and your solution will depend very much on usage.

      Do I think deprecating them is a good idea... it would be a cold day in hell if I said yes. They are legitimate mechanisms for controlling threads (and as I say, ones I've used myself on numerous occasions with no problems). So, I've just gone looking for an answer. I think this excerpt sums it up nicely (taken from this article by Peter Richie):-

      For time eternal there has been a way to suspend (and by association resume) another thread. It wasn't until .NET 2.0 that someone took leadership and conceded that suspending another thread is not a safe thing to do. .NET 2.0 deprecates System.Threading.Thread.Suspend() and Resume() (although, Resume() isn't dangerous by association...).

      Basically, Suspend() has no care that the thread may actually be managing one or more complex invariants that can't be changed atomically. Normally with complex invariants synchronization is used to ensure while one thread is modifying the individual components of an invariant another thread doesn't try to access it. This is important because the validity of an invariant is rarely atomic; meaning changing an invariant may take several steps and may be in an unknown or invalid state between the first and the last step (a date is a good example, setting the day and month is two steps; until both steps are complete the date might be invalid). Suspending a thread circumvents the synchronization primitives and cuts the thread off at the knees.
      Basically, calling suspend from outside a thread is bad... well all I can say is "there's a suprise". I've never done it and I wouldn't encourage you to do it for the reasons Peter gives.... the caller of suspend has no idea about the status of the thread. So, maybe a better solution would to have been to change visibility of the suspend method so that it was only accessible to the thread itself.

      Who knows, but I certainly don't agree with removing them as it's going to mean we have some changes to do at work when we finally port to an up to date Delphi version.
Comodo SSL