Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Very Simple Multithreading

multithread multithreading

  • Please log in to reply
No replies to this topic

#1 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1320 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 14 July 2012 - 08:29 PM

This tutorial serves to introduce simple multithreading in Delphi. Sample codes in this tutorial and the demo project was written with Delphi 7. Although it has not been properly tested, but the codes should be compatible with other Delphi version and FreePascal/Lazarus.


Multithreading, the Introduction

In Windows, and I believe also in any other modern multitasking operating systems, codes inside a program is nothing but a file unless they got loaded into memory. A loaded program in Windows is called a process. A process is a bit similar with multitasking operating system. Because it can execute several chains of codes at the same time. We call each chain of codes as thread. Codes are executed in the context of thread.

A process must have at least one thread, and a process may have many threads. Codes inside the same thread is blocking to each other. Meaning that if A and B are codes inside the same thread, and B's place is after A, then B must wait until A finished execution (returned) before it can start. It is different with codes of different thread. They are independent to each other. For example, if A and B are codes located in different thread, A and B might be executing in the same time.

The possibility to execute codes at the same time in the same process usually used to avoid the blocking of the main thread to wait for long time operations (such as I/O operation like reading disks). Operations that will take relatively long time could be assigned to different thread to make the operation not blocking the original thread execution. The result of the operation can be synchronized with the original thread either by interrupt or polling approach.

In Delphi GUI application, the GUI is maintained by the main thread. So if your code is blocking the main thread, your GUI will also not be able to be refreshed, which will make your application looks like hung. If you ever faced this kind of problem, you should consider to relocate the blocking codes to a separate thread. Properly done, it will banish your "hung" problem.

Of course multithreading requires proper synchronization. Because it opens the possibility of multiple operation accessing the same resource at the same time. When this is happening we want the state of the resource to be consistent.


TThread

Delphi has a nice framework for multithreading. There is a class named TThread which nicely wraps the APIs to create new thread and execute codes in it that makes it easy to work in separate thread.


Demo Project

Requirements

In our demo project we have these missions:
  • When told to, play beeping sound continuously.
  • Gui must not look hung.
  • When told to, the beeping sound must stop immediately.


GUI Preparation

  • Create a new application. Save it under any name you like. I left mine as is, i.e. Project1.dpr.
  • The application should already have a form named Form1
  • Drop a TButton to Form1's top area. Name it btnStartBeepMultithread. Set its Caption to Start Beeping (Multithread). Adjust its width accordingly.
  • Drop another TButton just below btnStartBeepMultithread. Name this button btnStartBeepNoThread. Give it caption of: Start Beeping (No multithread). Adjust its size accordingly.
  • Drop another TButton below btnStartBeepNoThread. Name this button btnStopBeep. Give it caption of: Stop Beeping. Adjust its size accordingly.

Adjust the form and the buttons so you get something like the following:

VerySimpleMultithreading_Form1Design001.png


Coding

Creating Beeper Thread

In the demo we want to place codes that sound the beeps inside separate thread, to get continous beeps without interfering with code execution in the main thread. Therefore we create a descendant of TThread, then override its Execute method and place the beeping codes inside it.

Let's name the new thread class as TBeeper. So declare it somewhere in the interface section of our Form1's unit. Declare it like:
type
  TBeeper=class(TThread)
  protected
    // the main body of the thread
    procedure Execute; override;
  end;


And put the beeping code in the Execute method implementation like this:

procedure TBeeper.Execute;
begin
  // execute codes inside the following block until the thread is terminated
  while not Terminated do
  begin
    // play beep sound
    Beep;
    // yield the processor to other process/thread
    Sleep(0);
    // check the flag
    if uStopBeeping then Exit;
  end;
end;

Note the uStopBeeping variable? We use it for flag to determine whether the beeping is still "playing" or has been stopped. It was a simple boolean variable local to the unit. So declare it somewhere in the implementation section of the unit.

Boolean variable of a unit usually initialized to false upon starting the program. In our case we want uStopBeeping starts with value of True since initially we don't sound the beep. We need to do that ini initialization of the unit. Place the following codes in the bottom area of the unit, just before the final end (end with a period).

initialization
  uStopBeeping := True;

To make it easier to use the continous beeping "feature", we provide two procedures to access the "feature". Declare the following procedures in interface section of the unit.

  procedure StartBeeping;
  procedure StopBeeping;

And their implementation:
procedure StartBeeping;
begin
  if not uStopBeeping then Exit;
  uStopBeeping := False;
  with TBeeper.Create(False) do
    // Tell the TBeeper instance to automatically destroy itself once it's been terminated
    FreeOnTerminate := True;
end;

procedure StopBeeping;
begin
  uStopBeeping := True;
end;


Coding the GUI

  • Double click on btnStartBeepMultithread button to generate its onclick event handler skeleton code. And call StartBeeping from there.
    procedure TForm1.btnStartBeepMultithreadClick(Sender: TObject);
    begin
      StartBeeping;
    end;
    
  • Double click on btnStartBeepNoThread button to generate its onclick event handler skeleton code, and use the following code for it.
    procedure TForm1.btnStartBeepNoThreadClick(Sender: TObject);
    begin
      uStopBeeping := False;
      // execute codes inside the following block until the thread is terminated
      while not uStopBeeping do
      begin
        // play beep sound
        Beep;
        // process pending messages might be in message queue
        Application.ProcessMessages;
      end;
    end;
    
  • Double click on btnStopBeep button to generate its onclick event handler skeleton code, and call StopBeeping from there.
    procedure TForm1.btnStopBeepClick(Sender: TObject);
    begin
      StopBeeping;
    end;
    


Run the Demo Project

Now the coding is done, time to test our demo program. Press F9 key to run our demo project, then you can experiment the beeping using multithreading and not.

Important Note:
Initially you can not notice the difference between beeping using multithreading and without one. That's because we were calling Application.ProcessMessages in beeping loop when we were not using multithreading. The calls executes GUI messages before re-sounding the beep. This made our GUI did not look hung. However if you were sounding the beep and then move the form around, the beep will cease to sound. This "bug" does not happen if you use beeping with multithreading.


Full source code of the demo project is attached.

Attached Files

  • Attached File  Demo.zip   196.67KB   8000 downloads

  • 0




Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download