In concurrent programming, concurrent accesses to shared resources can lead to unexpected or erroneous behavior. Thus, the parts of the program where the shared resource is accessed need to be protected in ways that avoid the concurrent access. One way to do so is known as a critical section or critical region. This protected section cannot be entered by more than one process or thread at a time; others are suspended until the first leaves the critical section. Typically, the critical section accesses a shared resource, such as a data structure, peripheral device, or network connection, that would not operate correctly in the context of multiple concurrent accesses.
Need for critical sections
Different code or processes may consist of the same variable or other resources that must be read or written but whose results depend on the order in which the actions occur. For example, if a variable is to be read by process A, and process B must write to the variable at the same time, process A might get either the old or new value of .
thumb|Flow graph depicting need for critical section
Process A:
<syntaxhighlight lang="c">
// Process A
.
.
b = x + 5; // instruction executes at time = Tx
.
</syntaxhighlight>
Process B:
<syntaxhighlight lang="c">
// Process B
.
.
x = 3 + z; // instruction executes at time = Tx
.
</syntaxhighlight>
In cases where a locking mechanism with finer granularity is not needed, a critical section is important. In the above case, if A needs to read the updated value of , executing process A, and process B at the same time may not give required results. To prevent this, variable is protected by a critical section. First, B gets the access to the section. Once B finishes writing the value, A gets the access to the critical section, and variable can be read.
By carefully controlling which variables are modified inside and outside the critical section, concurrent access to the shared variable is prevented. A critical section is typically used when a multi-threaded program must update multiple related variables without a separate thread making conflicting changes to that data. In a related situation, a critical section may be used to ensure that a shared resource, for example, a printer, can only be accessed by one process at a time.
Implementation of critical sections
The implementation of critical sections vary among different operating systems.
A critical section will usually terminate in finite time, and a thread, task, or process must wait for a fixed time to enter it (bounded waiting). To ensure exclusive use of critical sections, some synchronization mechanism is required at the entry and exit of the program.
A critical section is a piece of a program that requires mutual exclusion of access.
thumb|381x381px|Locks and critical sections in multiple threads
As shown in the figure, in the case of mutual exclusion (mutex), one thread blocks a critical section by using locking techniques when it needs to access the shared resource, and other threads must wait their turn to enter the section. This prevents conflicts when two or more threads share the same memory space and want to access a common resource. Most processors provide the required amount of synchronization by interrupting the current execution state. This allows critical sections in most cases to be nothing more than a per processor count of critical sections entered.
Performance enhancements include executing pending interrupts at the exit of all critical sections and allowing the scheduler to run at the exit of all critical sections. Furthermore, pending interrupts may be transferred to other processors for execution.
Critical sections should not be used as a long-lasting locking primitive. Critical sections should be kept short enough so they can be entered, executed, and exited without any interrupts occurring from the hardware and the scheduler.
Kernel-level critical sections are the base of the software lockout issue.
Critical sections in data structures
In parallel programming, the code is divided into threads. The read-write conflicting variables are split between threads and each thread has a copy of them. Data structures such as linked lists, trees, and hash tables have data variables that are linked and cannot be split between threads; hence, implementing parallelism is very difficult. To improve the efficiency of implementing data structures, multiple operations such as insertion, deletion, and search can be executed in parallel. While performing these operations, there may be scenarios where the same element is being searched by one thread and is being deleted by another. In such cases, the output may be erroneous. The thread searching the element may have a hit, whereas the other thread may subsequently delete it. These scenarios will cause issues in the program running by providing false data. To prevent this, one method is to keep the entire data structure under critical section so that only one operation is handled at a time. Another method is locking the node in use under critical section, so that other operations do not use the same node. Using critical section, thus, ensures that the code provides expected outputs.
