Multi-threading with TThread
- Part II. Critical Section¶
Why We Need Critical Sections¶
Imagine you and your friends are working on a group project, and you all need to update a shared document. If everyone tries to edit the document at the same time, changes might get lost or mixed up. To prevent this, you take turns updating the document. This is similar to what happens in a program when multiple parts (threads) need to access and modify shared data.
The Problem with Concurrent Access¶
In a program, you might have multiple threads running at the same time, each trying to read or write to the same piece of data. If this access isn’t controlled, it can lead to:
- Data Corruption: Two threads might try to update the data simultaneously, leading to incorrect results.
- Inconsistent State: The shared data might end up in an unpredictable state, causing bugs that are hard to find and fix.
What is a Critical Section?¶
A critical section is a part of your code that you want to protect so that only one thread can execute it at a time. It’s like setting up a rule where only one person can edit the document at a time to ensure that changes are made safely and correctly.
How it Works in Free Pascal¶
In Free Pascal, you use critical sections to control access to shared data.
Critical Section as a Lock: Think of a critical section as a lock that you put around the shared document. When someone wants to edit the document, they have to take the lock, do their changes, and then give the lock back. This ensures that only one person can edit at a time.
How to Use Critical Sections
-
EnterCriticalSection
: Before running the code you want to protect, callEnterCriticalSection
. This ensures that the current thread is the only one running the protected code. This call may make the thread wait until it's safe to proceed. -
LeaveCriticalSection
: After the protected code has finished, callLeaveCriticalSection
. This allows other threads to run the protected code.
Tip
To minimise waiting time for threads, keep the protected code block as small as possible.
Setup and Cleanup
InitCriticalSection
: Initializss the critical section. Must be called before usingEnterCriticalSection
orLeaveCriticalSection
.DoneCriticalSection
: Cleans up the critical section. Must be called when you're done with the critical section.
Here is an example of using Critical Section to increment a counter variable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
|
Explanation of the Code¶
-
Declare and Initialise:
criticalSection
is declared as a variable of typeTRTLCriticalSection
. Line 47.InitCriticalSection(criticalSection)
initialises the critical section (lock). Line 104.
-
Enter and Leave Critical Section:
EnterCriticalSection(criticalSection)
is like taking the lock. Only one thread can do this at a time. Line 70.LeaveCriticalSection(criticalSection)
releases the lock, allowing another thread to take it. Line 82.
-
Protecting Shared Data:
- The
IncrementCounter
procedure increments the counter safely because the code inside the critical section can only be executed by one thread at a time. Line 146.
- The
-
Cleanup:
DoneCriticalSection(criticalSection)
cleans up the critical section when it's no longer needed. Line 130.