Process and Thread Synchronization
Introduction
When multiple threads or processes access shared resources, their access must be coordinated to avoid concurrency issues.
Concurrency Issues
Race Condition
When the result depends on the undetermined order of execution.
Thread 1 Thread 2
-------- --------
Read counter (= 5)
Read counter (= 5)
Increment (= 6)
Increment (= 6)
Write (= 6)
Write (= 6)
Result: 6 instead of 7!
Critical Section
Code area that accesses a shared resource and must be executed by only one thread at a time.
Deadlock
Situation where two threads wait for each other indefinitely.
Thread 1: Holds A, waits for B
Thread 2: Holds B, waits for A
-> Permanent block!
Windows Synchronization Mechanisms
Overview
| Mechanism | Scope | Usage |
|---|---|---|
| Critical Section | Intra-process | Fast access, same process |
| Mutex | Inter-process | Mutual exclusion, named |
| Semaphore | Inter-process | Multiple access control |
| Event | Inter-process | Signaling |
| Interlocked | Intra-process | Simple atomic operations |
Critical Section
Concept
Lightweight mechanism to synchronize threads within the same process.
Characteristics
- Faster than Mutex
- Limited to a single process
- Unnamed
Usage (C/C++)
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
EnterCriticalSection(&cs);
// Critical section - one thread at a time
counter++;
LeaveCriticalSection(&cs);
DeleteCriticalSection(&cs);
In PowerShell (simulation)
# Using [System.Threading.Monitor]
$lockObj = New-Object Object
$counter = 0
[System.Threading.Monitor]::Enter($lockObj)
try {
# Critical section
$counter++
}
finally {
[System.Threading.Monitor]::Exit($lockObj)
}
Mutex (Mutual Exclusion)
Concept
Synchronization object allowing mutual exclusion between processes.
Characteristics
- Can be named (accessible between processes)
- Slower than Critical Section
- Deadlock detection possible
Usage in PowerShell
# Create or open a named mutex
$mutexName = "Global\MyMutex"
$mutex = New-Object System.Threading.Mutex($false, $mutexName)
try {
# Wait for the mutex (timeout 5 seconds)
if ($mutex.WaitOne(5000)) {
try {
Write-Host "Mutex acquired - critical section"
# Exclusive work here
Start-Sleep -Seconds 2
}
finally {
$mutex.ReleaseMutex()
Write-Host "Mutex released"
}
}
else {
Write-Host "Timeout - mutex not acquired"
}
}
finally {
$mutex.Close()
}
Named vs Anonymous Mutex
| Type | Name | Scope |
|---|---|---|
| Anonymous | None | Threads of the same process |
| Local | "Local\Name" | User session |
| Global | "Global\Name" | All system processes |
Semaphore
Concept
Allows limiting the number of simultaneous accesses to a resource.
Difference with Mutex
| Mutex | Semaphore |
|---|---|
| 1 access only | N simultaneous accesses |
| Single owner | No owner |
Usage in PowerShell
# Semaphore with 3 slots
$semName = "Global\MySemaphore"
$semaphore = New-Object System.Threading.Semaphore(3, 3, $semName)
try {
if ($semaphore.WaitOne(5000)) {
try {
Write-Host "Slot acquired in semaphore"
# Work limited to 3 simultaneous threads
Start-Sleep -Seconds 2
}
finally {
$semaphore.Release()
Write-Host "Slot released"
}
}
}
finally {
$semaphore.Close()
}
Use Cases
- Database connection pool
- Limiting simultaneous downloads
- Control of limited resources
Event
Concept
Allows a thread to signal an event to other threads.
Event Types
| Type | Behavior |
|---|---|
| AutoReset | Releases one thread then resets to non-signaled |
| ManualReset | Remains signaled until explicit reset |
Usage in PowerShell
# ManualReset Event
$eventName = "Global\MyEvent"
$event = New-Object System.Threading.EventWaitHandle(
$false, # Initial state: non-signaled
[System.Threading.EventResetMode]::ManualReset,
$eventName
)
# In the producer thread
$event.Set() # Signal
$event.Reset() # Reset to non-signaled
# In the consumer thread
if ($event.WaitOne(5000)) {
Write-Host "Event received!"
}
$event.Close()
Interlocked Operations
Concept
Atomic operations on variables, without the need for explicit lock.
Available Operations
| Operation | Description |
|---|---|
| Increment | Increments by 1 |
| Decrement | Decrements by 1 |
| Add | Adds a value |
| Exchange | Exchanges values |
| CompareExchange | Conditional exchange |
Usage in PowerShell
# Atomic increment
$counter = 0
[System.Threading.Interlocked]::Increment([ref]$counter)
Write-Host "Counter: $counter"
# Atomic add
[System.Threading.Interlocked]::Add([ref]$counter, 10)
Write-Host "Counter after add: $counter"
# Atomic exchange
$oldValue = [System.Threading.Interlocked]::Exchange([ref]$counter, 100)
Write-Host "Old value: $oldValue, New: $counter"
Best Practices
Avoid Deadlocks
- Consistent order: Always acquire locks in the same order
- Timeouts: Use timeouts to detect blocks
- Minimize: Keep critical sections short
Example of avoided deadlock
# BAD - risk of deadlock
# Thread 1: Lock A then Lock B
# Thread 2: Lock B then Lock A
# GOOD - consistent order
# Thread 1: Lock A then Lock B
# Thread 2: Lock A then Lock B (same order!)
Performance
| Mechanism | Performance | Use Case |
|---|---|---|
| Interlocked | Very fast | Simple operations |
| Critical Section | Fast | Same process |
| Mutex | Medium | Inter-process |
| Semaphore | Medium | Limited multiple access |
Problem Detection
Process Explorer
- View > Show Lower Pane
- Handle view: see synchronization handles
WinDbg
!locks # Display locks
!handle # Display handles
!analyze -hang # Analyze a hang/deadlock
Performance Monitor
Useful counters:
Process > Handle CountThread > Context Switches/sec
Exercises
- Create a script with two PowerShell jobs using a shared mutex
- Implement a thread-safe counter with Interlocked
- Create a semaphore that limits to 2 simultaneous executions