0% found this document useful (0 votes)
45 views39 pages

SPThreads 4 Cond

The document discusses using condition variables and mutexes to synchronize threads in a producer-consumer problem with a bounded buffer. A condition variable allows a consumer thread to wait if the buffer is empty until the producer thread signals that data is available, avoiding busy waiting. The example shows a producer thread blocking on the condition variable when the buffer is full, until the consumer frees space by consuming an item.

Uploaded by

loopnumber2020
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
45 views39 pages

SPThreads 4 Cond

The document discusses using condition variables and mutexes to synchronize threads in a producer-consumer problem with a bounded buffer. A condition variable allows a consumer thread to wait if the buffer is empty until the producer thread signals that data is available, avoiding busy waiting. The example shows a producer thread blocking on the condition variable when the buffer is full, until the consumer frees space by consuming an item.

Uploaded by

loopnumber2020
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 39

(T3) Threads: POSIX Condition

Ion Mandoiu
Laurent Michel
Revised by M. Khan and J. Shi
Mutex Review

• Mutex
• One has to get the lock before entering critical section
• Yields exclusive access to a resource
• If a thread cannot get the lock, it waits …
• Example of two threads sharing data
• Thread 1 computes a result
• Thread 2 displays the result on UI when computation done
• Question
How does thread 2 know the result is ready?

2
Producer-Consumer with Bounded Buffer

• Classic problem
• Producer(s) put things into a shared buffer
• Consumer(s) take them out!

3
Problem Constraints

• Mutual exclusion
• Only one thread can manipulate the buffer at any time
• Buffer is shared!
• Synchronization
• Consumer(s) must WAIT if buffer is empty
• Producer(s) must WAIT if buffer is full

Can it be done with only a lock ?

4
Well…. Sorta…
Consumer in pseudo-code

do {
Lock the buffer // Buffer is shared ! May have to wait
if buffer is not empty
Fetch data in the buffer
Unlock the buffer
} while (data is not fetched) // May fail, so try in a loop
Process the data

5
Consumer in C-like code
pthread_mutex_t buffer_lock;
// Consumer fetches data from a buffer, one each time.
fetched = 0;
do {
pthread_mutex_lock(& buffer_lock); // Get the lock for the buffer
if (nElements > 0) { // If the buffer is not empty
data = fetch_from_buffer(); // Fetch data from the buffer
fetched = 1;
}
pthread_mutex_unlock(& buffer_lock);
} while (fetched == 0);
// Continue to process data

What happens if the buffer is empty ?


6
Condition variable?

• A handshake mechanism to say: “There is data for you to use”

7
Consumer waits on condition
pthread_mutex_t buffer_lock;
// Consumer fetches data from a buffer, one each time.

pthread_mutex_lock(& buffer_lock); // Get the lock for the buffer


while (! nElements > 0) { // keep waiting if buffer is empty
wait for someone telling me data is ready!
}
data = fetch_from_buffer(); // fetch data from the buffer
pthread_mutex_unlock(& buffer_lock);

// Continue to process data

8
Consumer waits on condition - 2

It is a while, not if
A predicate

pthread_mutex_lock(& buffer_lock); // Get the lock for the buffer


while (! nElements > 0) { // keep waiting if buffer is empty
wait for someone telling me data is ready!
}
data = fetch_from_buffer(); // fetch data from the buffer
pthread_mutex_unlock(& buffer_lock);

Check predicate when mutex is locked


mutex is unlocked while waiting,
and locked when waiting is over
• Predicate: Logic expression describing the property of an object (or the state) the program
needs
• Examples: the buffer is full, or the buffer is not empty 9
POSIX condition variable

• Second and last core synchronization primitive


• All others (barriers, read-write locks, …) can be implemented using
mutexes and condition variables
• Always paired with a mutex
• Its access needs to be mutex protected!
• If a predicate is not true, a thread can wait on a condition variable
• Other threads can signal on the condition variable when the predicate has
changed
• Threads must still check if the predicate is true due to spurious wake-ups

10
pthread_cond API

#include <pthread.h>

pthread_cond_t cond; // Define a condition variable


int pthread_cond_init(pthread_cond_t * cond,
const pthread_condattr_t * attr);
int pthread_cond_destroy(pthread_cond_t *cond);

int pthread_cond_wait(pthread_cond_t *cond,


pthread_mutex_t *mutex);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

11
Tips

• The mutex protects


• The shared data and the state (predicate P)
• The condition variable
• lock the mutex before waiting
• the mutex is unlocked automatically while waiting
• automatically and atomically re-lock the mutex when waking up
• The predicate P is checked in a while loop!
• The while loop returns to waiting when P is false
• Why? (You will see in a moment)

12
Typical structure of using mutex and condition
pthread_mutex_t mutex;
pthread_cond_t cond; // mutex and cond are defined somewhere

pthread_mutex_lock(&mutex); // Get the mutex


while (! predicate ) { // check the predicate
pthread_cond_wait(&cond, &mutex); // wait on the condition, mutex is unlocked!
do_something(); // if needed. mutex is locked here!!!
}
access_shared_resources(); // safe to access the share resources
pthread_mutex_unlock(&mutex);

// continue to perform other tasks, for example, process fetched data.


// Note that access_shared_resources() should be short.

13
Example setup

• Assumptions
• A buffer that can hold only one item
• A predicate P, indicating if the data is ready for consumer
• A POSIX condition variable
• A POSIX mutex

• Threads
T1: producer T2: consumer
Can be many threads: 1 .. k

14
Two scenarios

• Scenario 1
• The producer gets to produce…
• …before the consumer consumes the previous one
• Scenario 2
• The consumer is eager and tries to consume…
• …before the producer gets a chance to produce

15
Scenario 1 : eager producer

• Producer tries to produce more, but the buffer is full

16
Storyboard
T1 TIME T2
Shared
Data
P
True
t e x
u
m

Condition C
Data is ready
P is True
T1 tries to place new data
17
Storyboard
T1 TIME T2
Shared
Data
CS P
True
t e x
u
m

Condition C

T1 enters CS
mutex is locked
18
Storyboard
T1 TIME T2
Shared
Data
CS P
while (P) wait(C,m) True
t e x
u
m

Condition C

Buffer is full
T1 waits on C
19
Storyboard
T1 TIME T2
Shared
Data
CS P
while (P) wait(C,m) True
t e x
u
m

Condition C

wait() unlocks mutex


Mutex is unlocked!
20
Storyboard
T1 TIME T2
Shared
Data
CS P
while (P) wait(C,m) True
t e x
u
m
CS
Condition C

T2 to consume
enters CS
21
Storyboard
T1 TIME T2
Shared
Data
CS P
while (P) wait(C,m) False
t e x
u
m
CS
Fetch data
Change P Condition C
signal(C)

Data fetched
T2 sends signal,
even if nobody is waiting 22
Storyboard
T1 TIME T2
Shared
Data
CS P
while (P) wait(C,m) False
t e x
u
m
CS
Fetch data
Change P Condition C
signal(C)

Process data
T2 unlock()
23
Storyboard
T1 TIME T2
Shared
Data
CS P
while (P) wait(C,m) False
t e x
u
m
CS
Fetch data
Change P Condition C
signal(C)

Process data
Continue to produce T1 gains the lock
24
Scenario 2 (Eager consumer)

• Consumer tries to fetch data before they are produced

25
Storyboard
T1 TIME T2
Shared
Data
P
False
t e x
u
m

Condition C

Buffer is empty
26
Storyboard
T1 TIME T2
Shared
Data
CS P
False
t e x
u
m

Condition C

T2 locks the mutex


and checks P
Data is not ready 27
Storyboard
T1 TIME T2
Shared
Data
CS P
while (!P) wait(C,m) False
t e x
u
m

Condition C

T2 has to wait!
wait() unlocks mutex
28
Storyboard
T1 TIME T2
Shared
Data
CS P
while (!P) wait(C,m) False
t e x
CS u
m

Condition C

T1 tries to place data


enters critical section
29
Storyboard
T1 TIME T2
Shared
Data
CS P
while (!P) wait(C,m) True
t e x
CS u
Set data m
Change P
signal(C)
Condition C

Data is ready
T1 sends signal
even if no one is waiting 30
Storyboard
T1 TIME T2
Shared
Data
CS P
while (!P) wait(C,m) True
t e x
CS u
Set data m
Change P
signal(C)
Condition C

Mutex unlocked
31
Storyboard
T1 TIME T2
Shared
Data
CS P
while (!P) wait(C,m) True
t e x
CS u
Set data m
Change P
signal(C)
Condition C
CS
Continue to consume T2 wakes up
and continues
32
Signal vs. Broadcast

pthread_cond_signal(pthread_cond_t *cond);
• Wakes up one waiting thread in the condition

pthread_cond_broadcast(pthread_cond_t *cond);
• Wakes up all waiting threads in the condition
• Yet, only one of the waiting threads can grab the mutex
• Go back to sleep if it fails

Caveat: a thread calling wait after the broadcast will not wake up (even if others in
the process of waking up are still stuck in the condition variable — e.g., waiting
on getting the mutex lock —)
33
Study the remaining slides yourself

34
Producer in pseudo-code

Prepare data
do {
Lock the buffer // May have to wait
if buffer is not full// For bounded buffer
Put data in the buffer
Unlock the buffer
} while (data is not placed in the buffer)

35
Producer in C-like code
pthread_mutex_t buffer_lock;
// Producer adds data in a buffer, one each time.
…… // Prepare data here
added = 0;
do {
pthread_mutex_lock(& buffer_lock); // Get the lock for the buffer
if (nElements < BUF_SIZE) { // If the buffer is not full
add_to_buffer (data); // Add data into the buffer
added = 1;
}
pthread_mutex_unlock(& buffer_lock);
} while (added == 0);

What happens if the buffer is full ?


36
Producer-Consumer Using Condition Variables
pthread_cond_t cond_queue_empty, cond_queue_full;
pthread_mutex_t task_queue_cond_lock;
int task_available;
/* other data structures here */
main()
{
/* declarations and initializations */
task_available = 0; // the predicate
pthread_cond_init(&cond_queue_empty, NULL);
pthread_cond_init(&cond_queue_full, NULL);
pthread_mutex_init(&task_queue_cond_lock, NULL);
/* create and join producer and consumer threads */

37
Producer-Consumer Using Condition Variables
void *producer(void *producer_thread_data)
{
int inserted;
while (!done()) {
create_task(); // time-consuming step, before locking the mutex
pthread_mutex_lock(&task_queue_cond_lock);
while (task_available == 1) // wait in a loop!
pthread_cond_wait(&cond_queue_empty,&task_queue_cond_lock);
insert_into_queue(); // access shared resources
task_available = 1; // update the predicate
pthread_cond_signal(&cond_queue_full);
pthread_mutex_unlock(&task_queue_cond_lock);
}
}
38
Producer-Consumer Using Condition Variables
void *consumer(void *consumer_thread_data)
{
while (!done()) {
pthread_mutex_lock(&task_queue_cond_lock);
while (task_available == 0) // wait in a loop!
pthread_cond_wait(&cond_queue_full,&task_queue_cond_lock);
my_task = extract_from_queue();
task_available = 0; // update the predicate
pthread_cond_signal(&cond_queue_empty);
pthread_mutex_unlock(&task_queue_cond_lock);
process_task(my_task); // this is after unlocking mutex
}
}
39

You might also like