Threads

From Lazarus wiki
Revision as of 03:03, 4 September 2018 by Kirinn (talk | contribs) (Tags)
Jump to navigationJump to search

Free Pascal supports thread programming, with a procedural and object-oriented interface that is mostly platform-neutral.

The official documentation is here: Programmer's guide chapter 10. It describes the procedural threading interface thoroughly.

The object-oriented interface TThread is described on this more comprehensive page: Multithreaded Application Tutorial


Notes on procedural thread management

BeginThread returns a thread handle directly, but also returns a thread ID as an output parameter. On Posix systems, there are no thread handles, so a copy of the thread ID is returned as both the ID and handle. On Windows, the thread handle is used for all thread management, while the ID is a separate unique value used as a thread enumerator. Therefore, regardless of platform, the thread handle returned by BeginThread is what all other RTL threading functions expect. (CloseThread, SuspendThread, WaitForThreadTerminate, etc)

GetCurrentThreadID returns specifically the thread ID, not the handle. So, while you could use this on Posix systems to call RTL threading functions, this would fail on Windows. To get the handle, you could use GetCurrentHandle or OpenThread in the Windows API, or retain the handle you got from BeginThread.

There are three steps in a thread's lifespan:

  • Another thread creates it by calling BeginThread.
  • The thread completes its work, then exits its top level function; or ends itself explicitly by calling EndThread; or is killed by another thread by calling KillThread.
  • Another thread detects that the thread has terminated, and calls CloseThread to clean up.

CloseThread does nothing on Posix systems, but on Windows it releases the thread handle. If a program keeps creating new threads but neglects to close them, this is a resource leak and could lead to system instability in extreme cases. All handles are released when the program terminates, but it's still good practice to explicitly call CloseThread as soon as a thread exits.

DoneThread and InitThread in the system unit are primarily for internal use, ignore them.

Avoid using KillThread if at all possible. If a thread happens to be blocking a manual synchronisation mechanism when it is terminated, any other thread waiting for that mechanism may be left waiting forever.

SuspendThread and ResumeThread are likewise dangerous, and not even supported on Posix systems, due to similar deadlocking concerns. Co-operative synchronisation is the safe alternative.

The system unit contains a GetCPUCount function, which is a convenient way to decide at runtime how many threads to create.


Thread synchronisation

The native synchronisation mechanisms in the system unit are: critical sections, RTL events, semaphores, WaitForThreadTerminate.

  • Critical sections are a co-operative code mutex, allowing only a single thread at a time into the protected code section, provided each thread enters and exits the section cleanly. This is a safe cross-platform way of protecting relatively small blocks of code.
  • RTL events are the preferred cross-platform synchronisation method. They block threads waiting for them, and when the event is set, all waiting threads are released.
  • Semaphores were Posix-only, and have been deprecated. They block threads waiting for them, and each SemaphorePost releases one waiting thread. Although semaphores are not available through the RTL, other implementations exist. (where?)
  • WaitForThreadTerminate blocks the current thread until the target thread has exited. The timeout parameter is ignored on Posix platforms, and the wait will never timeout.

WaitForSingleObject and WaitForMultipleObjects famously do not exist under Linux, so they're not good for cross-platform use. They can be of some use under Windows.

Cross-process communication requires a more robust solution, as thread synchronisation mechanisms are insufficient. SimpleIPC may be appropriate for this.