0% found this document useful (0 votes)
2 views12 pages

Parallel Lab 3

This document covers advanced thread synchronization techniques, focusing on reentrancy with RLocks, the Producer-Consumer problem, and various synchronization primitives like Semaphores and Conditions. It explains how RLocks prevent deadlocks by allowing a thread to acquire a lock multiple times and details how to implement the Producer-Consumer pattern using Semaphores and Conditions. The document emphasizes writing robust, thread-safe code and using appropriate tools for concurrency challenges.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views12 pages

Parallel Lab 3

This document covers advanced thread synchronization techniques, focusing on reentrancy with RLocks, the Producer-Consumer problem, and various synchronization primitives like Semaphores and Conditions. It explains how RLocks prevent deadlocks by allowing a thread to acquire a lock multiple times and details how to implement the Producer-Consumer pattern using Semaphores and Conditions. The document emphasizes writing robust, thread-safe code and using appropriate tools for concurrency challenges.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Parallel Processing

Lab 3: Advanced Thread Synchronization

Eng: Ebrahim Elgazar


OBJECTIVES FOR TODAY

Understand Reentrancy: What is an RLock and why a


normal Lock can cause deadlock.
Solve the Producer-Consumer Problem: A classic
concurrency pattern.
Master Synchronization Primitives:
Semaphore: Manage access to a pool of resources.
Condition: Wait for complex state changes.
Event: Simple, one-way signaling.
Use the Right Tool: Learn why Queue is often the best
solution for producer-consumer scenarios.
Write robust, thread-safe code.

2
RECAP - THE PROBLEM WITH [Link]

A simple Lock provides mutual exclusion. Only one thread can hold the lock at a time.

lock = [Link]()

def critical_section():
[Link]()
try:
# ... do work on shared resource ...
finally:
[Link]()

But what if a function that holds a lock calls another function that tries to acquire the same
lock?
3
DEADLOCK WITH A SIMPLE LOCK

lock = [Link]()

def outer_function():
print("Thread trying to acquire lock...")
[Link]() # Success!
print("Thread acquired lock.")

inner_function() # This function also needs the


lock

[Link]()
PROBLEM: THE THREAD HOLDS THE LOCK

def inner_function(): AND IS NOW WAITING FOR ITSELF TO


print("Thread trying to acquire lock AGAIN...") RELEASE IT. IT WILL WAIT FOREVER.
[Link]() # DEADLOCK!
print("This will never be printed.")
[Link]() 4
THE SOLUTION: REENTRANT LOCK (RLOCK)

An RLock solves this problem. It can be acquired multiple times by the same
thread.

How it works:
It tracks the "owning" thread.
It maintains a recursion level counter.
First acquire() by Thread A:
Owner -> Thread A
Counter -> 1
Second acquire() by Thread A:
Owner is still Thread A, so no blocking.
Counter -> 2
First release() by Thread A:
Counter -> 1
Second release() by Thread A:
Counter -> 0
The lock is now fully released and available to other threads. 5
THE SOLUTION: REENTRANT LOCK (RLOCK)

import threading

rlock = [Link]()
shared = 0

AN RLOCK SOLVES THIS PROBLEM. IT CAN BE def func():


ACQUIRED MULTIPLE TIMES BY THE SAME global shared
THREAD. # Outer critical section
[Link]()
try:
# Inner critical section
KEY TAKEAWAY: USE RLOCK WHEN A THREAD
MAY NEED TO RE-ACQUIRE A LOCK IT ALREADY [Link]()
HOLDS, SUCH AS IN RECURSIVE OR NESTED try:
FUNCTIONS. shared += 1
finally:
[Link]() # Releases inner acquisition
finally:
[Link]() # Releases outer acquisition

6
THE PRODUCER-CONSUMER PROBLEM

A fundamental pattern in concurrent programming.


Producer: Creates data and puts it into a shared buffer.
Consumer: Takes data from the shared buffer and processes it.
Shared Buffer: A temporary storage space (e.g., a variable, a list, a queue).
Synchronization Challenges:
The Consumer must not consume from an empty buffer.
The Producer must not add to a full buffer (if size is limited).

7
SEMAPHORE

A Semaphore is a counter that controls access to a limited number of resources.


[Link](): Decrements the counter. Blocks if counter is zero.
[Link](): Increments the counter. Wakes up a waiting thread.
Analogy: A Parking Garage
Semaphore(5) is a garage with 5 spots.
acquire() is a car entering. If full, it waits.
release() is a car leaving, freeing up a spot.
Binary Semaphore: Semaphore(1) behaves like a Lock.

7
SOLVING PRODUCER-CONSUMER WITH A
SEMAPHORE

# Semaphore starts at 0. The consumer will block


This is a clever signaling mechanism: immediately.
item_available = [Link](0)
We need to signal when an item is available shared_item = None
for consumption.
class Producer(Thread):
We'll use a semaphore initialized to zero. def run(self):
# ... produce an item ...
shared_item = item
print(f"Produced: {item}")
item_available.release() # Signal: "An item is
ready!" (Counter -> 1)
The Consumer calls acquire() first and blocks.
The Producer produces an item and calls class Consumer(Thread):
release(), which unblocks the consumer. def run(self):
This creates perfect alternation. print("Consumer is waiting...")
item_available.acquire() # Waits until counter >
0
print(f"Consumed: {shared_item}")

6
CONDITION

Condition object bundles a Lock with a waiting mechanism.


More expressive than a Semaphore for complex conditions.
Key Methods:
[Link]() / [Link](): Manages the underlying lock.
[Link]():
Releases the lock.
Puts the thread to sleep.
Wakes up and re-acquires the lock when notified.
[Link](): Wakes up one waiting thread.
cond.notify_all(): Wakes up all waiting threads.

7
PRODUCER-CONSUMER WITH CONDITION

condition = [Link]()
shared_item = None
class Producer(Thread):
def run(self):
with condition:
# ... produce an item ...
The canonical pattern for shared_item = item
print(f"Produced: {item}")
wait() is a while loop to guard [Link]() # Wake up the consumer
against "spurious wakeups". class Consumer(Thread):
def run(self):
with condition:
while shared_item is None:
print("Consumer waiting...")
[Link]() # Releases lock and waits
print(f"Consumed: {shared_item}")
shared_item = None

7
The End
Thank Your For Listening

You might also like