tthread

How to Initialize the thread?

Use the constructor to initialize your new thread class. This is where you can assign a default priority for your thread and indicate whether it should be freed automatically when it finishes executing.

How to assign a default priority?

Priority indicates how much preference the thread gets when the operating system schedules CPU time among all the threads in your application. Use a high priority thread to handle time critical tasks, and a low priority thread to perform other tasks. To indicate the priority of your thread object, set the Priority property.

If writing a Windows application, Priority values fall along a scale, as described in the following table:

Value Priority
tpIdle The thread executes only when the system is idle. Windows won't interrupt other threads to execute a thread with tpIdle priority.
tpLowest The thread's priority is two points below normal.
tpLower The thread's priority is one point below normal.
tpNormal The thread has normal priority.
tpHigher The thread's priority is one point above normal.
tpHighest The thread's priority is two points above normal.
tpTimeCritical The thread gets highest priority.

Note: If writing a cross-platform application, you must use separate code for assigning priorities on Windows and Linux. On Linux, Priority is a numeric value that depends on the threading policy which can only be changed by root. See the CLX version of TThread and ~JMPPriority ~!Alink(tthread_priority,1,TopicNotFound,main) JMP~> online Help for details.

Warning:  Boosting the thread priority of a CPU intensive operation may "starve" other threads in the application. Only apply priority boosts to threads that spend most of their time waiting for external events.

The following code shows the constructor of a low-priority thread that performs background tasks which should not interfere with the rest of the application's performance:

//---------------------------------------------------------------------------

__fastcall TMyThread::TMyThread(bool CreateSuspended): TThread(CreateSuspended)
{
  Priority = tpIdle;
}

//---------------------------------------------------------------------------

Indicating when threads are freed

Usually, when threads finish their operation, they can simply be freed. In this case, it is easiest to let the thread object free itself. To do this, set the FreeOnTerminate property to true.

There are times, however, when the termination of a thread must be coordinated with other threads. For example, you may be waiting for one thread to return a value before performing an action in another thread. To do this, you do not want to free the first thread until the second has received the return value. You can handle this situation by setting FreeOnTerminate to false and then explicitly freeing the first thread from the second.

How to write the thread function?

The Execute method is your thread function. You can think of it as a program that is launched by your application, except that it shares the same process space. Writing the thread function is a little trickier than writing a separate program because you must make sure that you don't overwrite memory that is used by other threads in your application. On the other hand, because the thread shares the same process space with other threads, you can use the shared memory to communicate between threads.

When implementing the Execute method, you can manage these issues by

  1. Using the main VCL/CLX thread
  2. Using thread-local variables
  3. Avoiding simultaneous access
  4. Waiting for other threads
  5. Checking for termination by other threads
  6. Handling exceptions in the thread function
 

Each service has its own thread (TServiceThread), so if your service application implements more than one service you must ensure that the implementation of your services is thread-safe. TServiceThread is designed so that you can implement the service in the TService OnExecute event handler. The service thread has its own Execute method which contains a loop that calls the service's OnStart and OnExecute handlers before processing new requests.

Because service requests can take a long time to process and the service application can receive simultaneous requests from more than one client, it is more efficient to spawn a new thread (derived from TThread, not TServiceThread) for each request and move the implementation of that service to the new thread's Execute method. This allows the service thread's Execute loop to process new requests continually without having to wait for the service's OnExecute handler to finish. The following example demonstrates.

Example: This service beeps every 500 milliseconds from within the standard thread. It handles pausing, continuing, and stopping of the thread when the service is told to pause, continue, or stop.

1. Choose File|New|Other and double-click Service Application in the New Items dialog. The Service1 window appears.
2. In you unit's header file, declare a new descendant of TThread named TSparkyThread. This is the thread that does the work for your service. The declaration should appear as follows:

class TSparkyThread : public TThread
{
private:
protected:
      void __fastcall Execute();
public:
        __fastcall TSparkyThread(bool CreateSuspended);
};

3. In the .cpp file for your unit, create a global variable for a TSparkyThread instance:

TSparkyThread *SparkyThread;

4. Add the following code to the .cpp file for the TSparkyThread constructor:

__fastcall TSparkyThread::TSparkyThread(bool CreateSuspended)
        : TThread(CreateSuspended)
{
}

5. Add the following code to the .cpp file for the TSparkyThread Execute method (the thread function):

void __fastcall TSparkyThread::Execute()
{
  while (!Terminated)
  {
    Beep();
    Sleep(500);
  }
}

6. Select the Service window (Service1), and double-click the OnStart event in the Object Inspector. Add the following OnStart event handler:

void __fastcall TService1::Service1Start(TService *Sender, bool &Started)
{
  SparkyThread = new TSparkyThread(false);
  Started = true;
}

7. Double-click the OnContinue event in the Object Inspector. Add the following OnContinue event handler:

void __fastcall TService1::Service1Continue(TService *Sender, bool &Continued)
{
  SparkyThread->Resume();
  Continued = true;
}

8. Double-click the OnPause event in the Object Inspector. Add the following OnPause event handler:

void __fastcall TService1::Service1Pause(TService *Sender, bool &Paused)
{
  SparkyThread->Suspend();
  Paused = true;
}

9. Finally, double-click the OnStop event in the Object Inspector and add the following OnStop event handler:

void __fastcall TService1::Service1Stop(TService *Sender, bool &Stopped)
{
  SparkyThread->Terminate();
  Stopped = true;
}

When developing server applications, choosing to spawn a new thread depends on the nature of the service being provided, the anticipated number of connections, and the expected number of processors on the computer running the service.