Difference between revisions of "Threads"

From Lazarus wiki
Jump to navigationJump to search
(Created page)
 
(Tags)
Line 1: Line 1:
{{Threads}}
 
 
 
Free Pascal supports thread programming, with a procedural and object-oriented interface that is mostly platform-neutral.
 
Free Pascal supports thread programming, with a procedural and object-oriented interface that is mostly platform-neutral.
  
 
The official documentation is here: [https://www.freepascal.org/docs-html/prog/progch10.html Programmer's guide chapter 10]. It describes the procedural threading interface thoroughly.
 
The official documentation is here: [https://www.freepascal.org/docs-html/prog/progch10.html 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]]
+
The object-oriented interface <code>TThread</code> is described on this more comprehensive page: [[Multithreaded Application Tutorial]]
  
  
Line 11: Line 9:
 
== Notes on procedural thread management ==
 
== 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)
+
<code>BeginThread</code> 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 <code>BeginThread</code> is what all other RTL threading functions expect. (<code>CloseThread</code>, <code>SuspendThread</code>, <code>WaitForThreadTerminate</code>, 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.
+
<code>GetCurrentThreadID</code> 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 <code>GetCurrentHandle</code> or <code>OpenThread</code> in the Windows API, or retain the handle you got from <code>BeginThread</code>.
  
 
There are three steps in a thread's lifespan:
 
There are three steps in a thread's lifespan:
* Another thread creates it by calling BeginThread.
+
* Another thread creates it by calling <code>BeginThread</code>.
* 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.
+
* The thread completes its work, then exits its top level function; or ends itself explicitly by calling <code>EndThread</code>; or is killed by another thread by calling <code>KillThread</code>.
* Another thread detects that the thread has terminated, and calls CloseThread to clean up.
+
* Another thread detects that the thread has terminated, and calls <code>CloseThread</code> 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.
+
<code>CloseThread</code> 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 <code>CloseThread</code> as soon as a thread exits.
  
DoneThread and InitThread in the system unit are primarily for internal use, ignore them.
+
<code>DoneThread</code> and <code>InitThread</code> 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.
+
Avoid using <code>KillThread</code> 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.
+
<code>SuspendThread</code> and <code>ResumeThread</code> 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 [https://www.freepascal.org/docs-html/rtl/system/getcpucount.html GetCPUCount] function, which is a convenient way to decide at runtime how many threads to create.
 
The system unit contains a [https://www.freepascal.org/docs-html/rtl/system/getcpucount.html GetCPUCount] function, which is a convenient way to decide at runtime how many threads to create.
Line 34: Line 32:
  
  
The native synchronisation mechanisms in the system unit are: critical sections, RTL events, semaphores, WaitForThreadTerminate.
+
The native synchronisation mechanisms in the system unit are: critical sections, RTL events, semaphores, <code>WaitForThreadTerminate</code>.
  
 
* 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.
 
* 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.
 
* 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?)
+
* Semaphores were Posix-only, and have been deprecated. They block threads waiting for them, and each <code>SemaphorePost</code> 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.
+
* <code>WaitForThreadTerminate</code> 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 are famously not available under Linux, so they're not good for cross-platform use. They can be of some use under Windows.
+
<code>WaitForSingleObject</code> and <code>WaitForMultipleObjects</code> 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. [https://www.freepascal.org/docs-html/fcl/simpleipc/index.html SimpleIPC] may be appropriate for this.
 
Cross-process communication requires a more robust solution, as thread synchronisation mechanisms are insufficient. [https://www.freepascal.org/docs-html/fcl/simpleipc/index.html SimpleIPC] may be appropriate for this.

Revision as of 02:03, 4 September 2018

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.