10.2 Programming threads

To start a new thread, the BeginThread function should be used. It has one mandatory argument: the function which will be executed in the new thread. The result of the function is the exit result of the thread. The thread function can be passed a pointer, which can be used to acces initialization data: The programmer must make sure that the data is accessible from the thread and does not go out of scope before the thread has accessed it.
Type  
  TThreadFunc = function(parameter : pointer) : longint;  
function BeginThread(ThreadFunction: tthreadfunc) : DWord;  
 
function BeginThread(ThreadFunction: tthreadfunc;  
                     p: pointer): DWord;  
function BeginThread(ThreadFunction: tthreadfunc;  
                     p: pointer;  
                     var ThreadId : THandle) : DWord;

If present, the pointer p will be passed to the thread function when it is started (otherwise, Nil is passed). If ThreadID is present, the ID of the thread will be stored in it.

The newly started thread will run until the ThreadFunction exits, or until it explicitly calls the EndThread function:

procedure EndThread(ExitCode : DWord);  
procedure EndThread;

The exitcode can be examined by the code which started the thread.

The following is a small example of how to program a thread:

{$mode objfpc}  
{$threading on}  
 
uses  
  sysutils {$ifdef unix},cthreads{$endif} ;  
 
const  
  threadcount = 100;  
  stringlen = 10000;  
 
var  
   finished : longint;  
 
threadvar  
   thri : longint;  
 
function f(p : pointer) : longint;  
 
var  
  s : ansistring;  
 
begin  
  Writeln('thread ',longint(p),' started');  
  thri:=0;  
  while (thri    begin  
    s:=s+'1';  
    inc(thri);  
    end;  
  Writeln('thread ',longint(p),' finished');  
  InterLockedIncrement(finished);  
  f:=0;  
end;  
 
var  
   i : longint;  
 
begin  
   finished:=0;  
   for i:=1 to threadcount do  
     BeginThread(@f,pointer(i));  
   while finished   Writeln(finished);  
end.

The InterLockedIncrement is a thread-safe version of the standard Inc function.

To provide system-independent support for thread programming, some utility functions are implemented to manipulate threads. To use these functions the thread ID must have been retrieved when the thread was started, because most functions require the ID to identify the thread on which they should act:

function SuspendThread(threadHandle: dword): dword;  
function ResumeThread(threadHandle: dword): dword;  
function KillThread(threadHandle: dword): dword;  
function WaitForThreadTerminate(threadHandle: dword;  
                                TimeoutMs : longint): dword;  
function ThreadSetPriority(threadHandle: dword;  
                           Prio: longint): boolean;  
function ThreadGetPriority(threadHandle: dword): Integer;  
function GetCurrentThreadId: dword;  
procedure ThreadSwitch;

The meaning of these functions should be clear:

SuspendThread
Suspends the execution of the thread.
ResumeThread
Resumes execution of a suspended thread.
KillThread
Kills the thread: the thread is removed from memory.
WaitForThreadTerminate
Waits for the thread to terminate. The function returns when the thread has nished executing, or when the timeout expired.
ThreadSetPriority
Sets the execution priority of the thread. This call is not always allowed.
ThreadGetPriority
Returns the current execution priority of the thread.
GetCurrentThreadId
Returns the ID of the current thread.
ThreadSwitch
Allows other threads to execute at this point, can cause a thread switch, but this is not guaranteed, it depends on the OS and the number of processors.