COBOL Tutorial

Progress0 of 0 lessons

COBOL Thread Synchronization

Thread synchronization is essential when multiple threads access shared resources concurrently in COBOL applications. Without proper synchronization, programs can experience race conditions, data corruption, and inconsistent states. This guide covers synchronization mechanisms including mutex locks, read/write locks, semaphores, condition variables, and best practices for safe concurrent programming.

Understanding Thread Synchronization

When multiple threads execute concurrently and access shared data, synchronization mechanisms coordinate their access to prevent conflicts. The goal is to ensure that:

  • Only one thread modifies shared data at a time (mutual exclusion)
  • Threads can safely read shared data when no modifications are occurring
  • Operations complete atomically without interruption
  • Deadlocks and race conditions are prevented

Synchronization Mechanisms

Common synchronization mechanisms
MechanismPurposeBest Use Case
Mutex LockAllows only one thread to access a resourceProtecting critical sections with exclusive access
Read/Write LockAllows multiple readers or one writerMany readers, few writers (e.g., configuration data)
SemaphoreLimits number of concurrent threadsControlling access to resource pools
Condition VariableBlocks threads until condition is metProducer-consumer patterns, waiting for events

Mutex Locks

A mutex (mutual exclusion) lock ensures that only one thread can execute a critical section at a time. When a thread acquires a mutex, other threads must wait until it's released.

Basic Mutex Pattern

cobol
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
WORKING-STORAGE SECTION. 01 LOCK-CONTROL. 05 LOCK-STATUS PIC X VALUE 'N'. 88 LOCK-ACQUIRED VALUE 'Y'. 88 LOCK-FREE VALUE 'N'. 05 LOCK-OWNER PIC X(8). 05 LOCK-TIMEOUT PIC 9(3) VALUE 30. 01 SHARED-RESOURCE. 05 SHARED-COUNTER PIC 9(8) VALUE 0. 05 SHARED-DATA PIC X(100). PROCEDURE DIVISION. *> Acquire lock before accessing shared resource PERFORM ACQUIRE-LOCK IF LOCK-ACQUIRED *> Critical section - modify shared data ADD 1 TO SHARED-COUNTER MOVE 'Updated data' TO SHARED-DATA *> Release lock after critical section PERFORM RELEASE-LOCK ELSE DISPLAY 'Failed to acquire lock' END-IF. ACQUIRE-LOCK. *> Attempt to acquire lock IF LOCK-FREE MOVE 'Y' TO LOCK-STATUS MOVE 'THREAD-01' TO LOCK-OWNER DISPLAY 'Lock acquired by ' LOCK-OWNER ELSE DISPLAY 'Lock is busy, waiting...' *> In real implementation, would wait or timeout PERFORM WAIT-FOR-LOCK END-IF. RELEASE-LOCK. IF LOCK-ACQUIRED MOVE 'N' TO LOCK-STATUS MOVE SPACES TO LOCK-OWNER DISPLAY 'Lock released' END-IF.

Protecting Critical Sections

cobol
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
PROCEDURE DIVISION. *> Protect file update operation PERFORM ENTER-CRITICAL-SECTION *> Critical section: update shared file READ SHARED-FILE INVALID KEY DISPLAY 'Record not found' NOT INVALID KEY COMPUTE NEW-VALUE = CURRENT-VALUE + INCREMENT MOVE NEW-VALUE TO CURRENT-VALUE REWRITE SHARED-RECORD INVALID KEY DISPLAY 'Update failed' NOT INVALID KEY DISPLAY 'Update successful' END-REWRITE END-READ *> Always exit critical section PERFORM EXIT-CRITICAL-SECTION. ENTER-CRITICAL-SECTION. PERFORM ACQUIRE-LOCK IF NOT LOCK-ACQUIRED DISPLAY 'Cannot enter critical section' PERFORM ERROR-HANDLING END-IF. EXIT-CRITICAL-SECTION. PERFORM RELEASE-LOCK.

Read/Write Locks

Read/write locks allow multiple threads to read simultaneously but only one thread to write at a time. This provides better concurrency than mutex locks when you have many readers and few writers.

cobol
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
WORKING-STORAGE SECTION. 01 READ-WRITE-LOCK. 05 READER-COUNT PIC 9(4) VALUE 0. 05 WRITER-ACTIVE PIC X VALUE 'N'. 88 WRITER-PRESENT VALUE 'Y'. 88 NO-WRITER VALUE 'N'. 05 LOCK-STATUS PIC X. 88 LOCK-ACQUIRED VALUE 'Y'. PROCEDURE DIVISION. *> Reader thread: acquire read lock PERFORM ACQUIRE-READ-LOCK IF LOCK-ACQUIRED *> Multiple readers can access simultaneously READ CONFIGURATION-FILE AT END DISPLAY 'End of file' NOT AT END DISPLAY 'Reading: ' CONFIG-DATA END-READ PERFORM RELEASE-READ-LOCK END-IF. ACQUIRE-READ-LOCK. *> Wait if writer is active PERFORM UNTIL NO-WRITER IF WRITER-PRESENT DISPLAY 'Waiting for writer to finish...' PERFORM WAIT-SHORT-TIME END-IF END-PERFORM *> Increment reader count ADD 1 TO READER-COUNT MOVE 'Y' TO LOCK-STATUS. RELEASE-READ-LOCK. SUBTRACT 1 FROM READER-COUNT IF READER-COUNT = 0 MOVE 'N' TO LOCK-STATUS END-IF. ACQUIRE-WRITE-LOCK. *> Wait until no readers and no other writer PERFORM UNTIL READER-COUNT = 0 AND NO-WRITER DISPLAY 'Waiting for readers/writers to finish...' PERFORM WAIT-SHORT-TIME END-PERFORM *> Acquire exclusive write lock MOVE 'Y' TO WRITER-ACTIVE MOVE 'Y' TO LOCK-STATUS. RELEASE-WRITE-LOCK. MOVE 'N' TO WRITER-ACTIVE MOVE 'N' TO LOCK-STATUS.

Semaphores

Semaphores control access to a resource by limiting the number of threads that can access it concurrently. Unlike mutex locks (which allow only one thread), semaphores can allow multiple threads up to a specified count.

cobol
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
WORKING-STORAGE SECTION. 01 SEMAPHORE-CONTROL. 05 SEMAPHORE-COUNT PIC 9(4) VALUE 5. 05 MAX-COUNT PIC 9(4) VALUE 5. 05 WAITING-COUNT PIC 9(4) VALUE 0. PROCEDURE DIVISION. *> Acquire semaphore (wait if count is 0) PERFORM SEMAPHORE-WAIT *> Access limited resource (e.g., connection pool) PERFORM USE-RESOURCE *> Release semaphore PERFORM SEMAPHORE-SIGNAL. SEMAPHORE-WAIT. *> Decrement count, wait if count becomes negative IF SEMAPHORE-COUNT > 0 SUBTRACT 1 FROM SEMAPHORE-COUNT DISPLAY 'Semaphore acquired, count: ' SEMAPHORE-COUNT ELSE *> Resource unavailable, must wait ADD 1 TO WAITING-COUNT DISPLAY 'Waiting for semaphore, ' WAITING-COUNT ' threads waiting' PERFORM WAIT-FOR-SEMAPHORE SUBTRACT 1 FROM WAITING-COUNT SUBTRACT 1 FROM SEMAPHORE-COUNT END-IF. SEMAPHORE-SIGNAL. *> Increment count, wake waiting threads if any ADD 1 TO SEMAPHORE-COUNT IF SEMAPHORE-COUNT > MAX-COUNT MOVE MAX-COUNT TO SEMAPHORE-COUNT END-IF IF WAITING-COUNT > 0 DISPLAY 'Signaling waiting thread, count: ' SEMAPHORE-COUNT PERFORM WAKE-WAITING-THREAD END-IF.

Deadlock Prevention

Deadlocks occur when threads are blocked forever, each waiting for a resource held by another. Prevent deadlocks by establishing consistent lock ordering.

Consistent Lock Ordering

cobol
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
PROCEDURE DIVISION. *> Always acquire locks in consistent order *> Use resource IDs to determine order IF RESOURCE-A-ID < RESOURCE-B-ID PERFORM LOCK-RESOURCE-A PERFORM LOCK-RESOURCE-B ELSE PERFORM LOCK-RESOURCE-B PERFORM LOCK-RESOURCE-A END-IF *> Perform operations on both resources PERFORM OPERATIONS-WITH-BOTH-RESOURCES *> Release locks in reverse order IF RESOURCE-A-ID < RESOURCE-B-ID PERFORM UNLOCK-RESOURCE-B PERFORM UNLOCK-RESOURCE-A ELSE PERFORM UNLOCK-RESOURCE-A PERFORM UNLOCK-RESOURCE-B END-IF. LOCK-RESOURCE-A. PERFORM ACQUIRE-LOCK-FOR-A IF NOT LOCK-ACQUIRED DISPLAY 'Failed to lock resource A' PERFORM HANDLE-LOCK-FAILURE END-IF. LOCK-RESOURCE-B. *> Only attempt if resource A is already locked IF RESOURCE-A-LOCKED PERFORM ACQUIRE-LOCK-FOR-B IF NOT LOCK-ACQUIRED *> Release A before failing PERFORM UNLOCK-RESOURCE-A DISPLAY 'Failed to lock resource B' PERFORM HANDLE-LOCK-FAILURE END-IF ELSE DISPLAY 'Error: Resource A not locked first' END-IF.

Timeout-Based Deadlock Detection

cobol
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
WORKING-STORAGE SECTION. 01 LOCK-TIMEOUT-CONTROL. 05 LOCK-TIMEOUT PIC 9(3) VALUE 30. 05 LOCK-START-TIME PIC 9(8). 05 CURRENT-TIME PIC 9(8). 05 ELAPSED-TIME PIC 9(8). PROCEDURE DIVISION. *> Record lock acquisition time ACCEPT LOCK-START-TIME FROM TIME PERFORM ACQUIRE-LOCK-WITH-TIMEOUT IF LOCK-ACQUIRED *> Check if we're still within timeout PERFORM CHECK-TIMEOUT IF TIMEOUT-EXCEEDED PERFORM RELEASE-LOCK DISPLAY 'Lock timeout exceeded, releasing' PERFORM HANDLE-TIMEOUT ELSE *> Proceed with critical section PERFORM CRITICAL-SECTION-OPERATIONS PERFORM RELEASE-LOCK END-IF END-IF. CHECK-TIMEOUT. ACCEPT CURRENT-TIME FROM TIME COMPUTE ELAPSED-TIME = CURRENT-TIME - LOCK-START-TIME IF ELAPSED-TIME > LOCK-TIMEOUT SET TIMEOUT-EXCEEDED TO TRUE ELSE SET TIMEOUT-OK TO TRUE END-IF.

Race Condition Prevention

Race conditions occur when the outcome depends on the timing of thread execution. Prevent them by using atomic operations and proper locking.

Avoiding Check-Then-Act Race Conditions

cobol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
*> PROBLEMATIC: Check-then-act pattern (race condition) IF COUNTER < MAX-LIMIT ADD 1 TO COUNTER *> Another thread might modify COUNTER here END-IF. *> BETTER: Atomic operation with lock PERFORM ACQUIRE-LOCK IF LOCK-ACQUIRED IF COUNTER < MAX-LIMIT ADD 1 TO COUNTER MOVE 'Y' TO INCREMENT-SUCCESSFUL ELSE MOVE 'N' TO INCREMENT-SUCCESSFUL END-IF PERFORM RELEASE-LOCK END-IF IF INCREMENT-SUCCESSFUL = 'Y' PERFORM CONTINUE-PROCESSING ELSE PERFORM HANDLE-LIMIT-REACHED END-IF.

Atomic Operations

cobol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*> Use atomic operations when possible *> ADD and MOVE are typically atomic ADD 1 TO RECORD-COUNT *> Atomic increment *> Avoid complex computations that might be interrupted *> BAD: COMPUTE RECORD-COUNT = RECORD-COUNT + 1 *> GOOD: ADD 1 TO RECORD-COUNT *> Atomic flag setting MOVE 'Y' TO PROCESSING-FLAG *> Atomic *> Thread-safe counter with lock PERFORM ACQUIRE-LOCK IF LOCK-ACQUIRED ADD INCREMENT-VALUE TO SHARED-COUNTER PERFORM RELEASE-LOCK END-IF.

Best Practices

1. Minimize Shared State

Reduce the amount of shared data between threads to minimize synchronization overhead and potential contention. Use local variables when possible.

2. Keep Critical Sections Short

Keep critical sections as short as possible to minimize the time locks are held, reducing contention and improving performance.

3. Always Release Locks

Always release locks in the same order they were acquired, and ensure locks are released even when errors occur (use exception handling).

4. Use Appropriate Lock Type

Choose the right synchronization mechanism: mutex for exclusive access, read/write locks for many readers, semaphores for resource pools.

5. Establish Lock Ordering

Always acquire multiple locks in the same order across all threads to prevent deadlocks. Use resource IDs or other consistent ordering criteria.

6. Use Timeouts

Implement timeouts for lock acquisition to detect and recover from deadlock situations, preventing indefinite blocking.

Common Patterns

Pattern 1: Producer-Consumer with Synchronization

cobol
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
WORKING-STORAGE SECTION. 01 QUEUE-CONTROL. 05 QUEUE-LOCK PIC X VALUE 'N'. 05 QUEUE-COUNT PIC 9(4) VALUE 0. 05 MAX-QUEUE-SIZE PIC 9(4) VALUE 100. PROCEDURE DIVISION. *> Producer: add items to queue PERFORM PRODUCER-LOOP UNTIL END-OF-DATA *> Consumer: remove items from queue PERFORM CONSUMER-LOOP UNTIL QUEUE-EMPTY. PRODUCER-LOOP. *> Wait if queue is full PERFORM UNTIL QUEUE-COUNT < MAX-QUEUE-SIZE PERFORM WAIT-SHORT-TIME END-PERFORM *> Acquire lock and enqueue PERFORM ACQUIRE-LOCK IF LOCK-ACQUIRED PERFORM ENQUEUE-ITEM ADD 1 TO QUEUE-COUNT PERFORM RELEASE-LOCK PERFORM SIGNAL-CONSUMER END-IF. CONSUMER-LOOP. *> Wait if queue is empty PERFORM UNTIL QUEUE-COUNT > 0 PERFORM WAIT-FOR-PRODUCER END-PERFORM *> Acquire lock and dequeue PERFORM ACQUIRE-LOCK IF LOCK-ACQUIRED PERFORM DEQUEUE-ITEM SUBTRACT 1 FROM QUEUE-COUNT PERFORM RELEASE-LOCK PERFORM PROCESS-ITEM END-IF.

Explain It Like I'm 5 Years Old

Imagine you and your friends want to use the same toy:

If everyone tries to grab the toy at the same time, it might break or someone might get hurt. So you make a rule: only one person can play with the toy at a time. When someone is done, they tell the next person it's their turn.

Thread synchronization is like making rules for sharing. A lock is like saying "I'm using this now" - when you have the lock, you can use the shared thing safely. When you're done, you give the lock to the next person.

Just like you take turns with toys, computer programs use locks to take turns with shared data, so everything stays safe and nothing gets broken!

Exercises

Exercise 1: Simple Counter with Mutex

Implement a thread-safe counter using a mutex lock. Multiple threads should be able to increment the counter safely.

cobol
1
2
3
4
5
6
*> Thread-safe counter increment PERFORM ACQUIRE-LOCK IF LOCK-ACQUIRED ADD 1 TO SHARED-COUNTER PERFORM RELEASE-LOCK END-IF.

Exercise 2: Read/Write Lock Implementation

Implement a read/write lock that allows multiple readers but only one writer at a time.

cobol
1
2
3
4
5
6
7
8
9
10
11
12
13
*> Reader: wait for no writer, increment reader count PERFORM UNTIL NO-WRITER IF WRITER-PRESENT PERFORM WAIT-SHORT-TIME END-IF END-PERFORM ADD 1 TO READER-COUNT *> Writer: wait for no readers and no other writer PERFORM UNTIL READER-COUNT = 0 AND NO-WRITER PERFORM WAIT-SHORT-TIME END-PERFORM MOVE 'Y' TO WRITER-ACTIVE.

Test Your Knowledge

1. What is the primary purpose of thread synchronization?

  • To make programs run faster
  • To coordinate access to shared resources and prevent data corruption
  • To simplify program logic
  • To reduce memory usage

2. What is a mutex lock?

  • A mechanism that allows multiple threads to access a resource
  • A mechanism that allows only one thread at a time to access a resource
  • A type of data structure
  • A file locking mechanism

3. What is the main difference between mutex locks and read/write locks?

  • Mutex locks are faster
  • Read/write locks allow multiple readers but only one writer
  • Read/write locks are simpler to use
  • There is no difference

4. What is a deadlock?

  • A fast execution path
  • A situation where threads are blocked forever waiting for each other
  • A type of lock
  • A synchronization primitive

5. How can you prevent deadlocks?

  • By using more locks
  • By establishing consistent lock ordering and using timeouts
  • By avoiding all locks
  • By using only one thread

Related Pages