0% found this document useful (0 votes)
36 views19 pages

Tut 05

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)
36 views19 pages

Tut 05

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/ 19

Condition variables via pthread Library in C

CSCI3150 Introduction to Operating Systems, Fall


2024

LI Jianqiang
[email protected]
Oct 3, 2024
Reminder

 Assignment 1
 Due at 18:00:00 p.m., Mon, Oct 7th
 Assignment 2
 Will release after tutorial, Oct 3th
 Due at 18:00:00 p.m., Mon, Nov 4th

2
Overview

 Tutorial this week


 Pthread Library part 2
 Key interfaces for condition variables
 Condition variable usage in C
 Multiple Producers and Consumers
 Barrier Synchronization
 Dining Philosophers Problem
All examples used in this tutorial:
https://github.com/henryhxu/CSCI3150/tree/
2024-Fall/tutorial/T05

3
Review: Pthread Library part 1

 The Pthread (POSIX threads) library is a powerful tool for creating and
managing threads in C. It provides a standardized API to facilitate multi-
threading, which is essential for developing concurrent applications.

To use it: #include <pthread.h>

 Key Interfaces:
 pthread_create  pthread_mutex_init
 pthread_exit  pthread_mutex_lock
 pthread_join  pthread_mutex_unlo
ck
 pthread_mutex_dest
roy
4
Pthread Library part 2

 The Pthread (POSIX threads) library is a powerful tool for creating and
managing threads in C. It provides a standardized API to facilitate multi-
threading, which is essential for developing concurrent applications.

To use it: #include <pthread.h>

 Key Interfaces:
 pthread_create  pthread_mutex_init  pthread_cond_init
 pthread_exit  pthread_mutex_lock  pthread_cond_wait
 pthread_join  pthread_mutex_unlo  pthread_cond_signal
ck  pthread_cond_broad
 pthread_mutex_dest cast
roy  pthread_cond_destro
y 5
1. pthread_cond_init

 Purpose: initialize a condition variable.


 Syntax:
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *at
tr);
 Parameters:
• cond: pointer to the condition variable.
• attr: optional attributes for the condition variable (use NULL for default).

 Example:
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

 Another way: using PTHREAD_COND_INITIALIZER


pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

6
2. pthread_cond_wait

 Purpose: wait for a condition variable to be signaled, releasing


the associated mutex.
 Syntax:
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

 Parameters:
• cond: pointer to the condition variable.
• mutex: pointer to an associated mutex (must be locked before calling).

 Example:
pthread_mutex_lock(&mutex);
while (condition_is_false) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

7
2. pthread_cond_wait

 Question: why a mutex will be passed into this function?

 Let’s see what has been done


inside:
pthread_cond_wait(pthread_cond_t* C, pthread_mutex_t* M)
{
<put this thread into wakeup queue of condtion var C.>
pthread_mutex_unlock(M);
sleep();
pthread_mutex_lock(M);
<take this thread out of wakeup queue of condition var C.>
}

8
3. pthread_cond_signal

 Purpose: wake up one thread waiting on a condition variable.


 Syntax:
int pthread_cond_signal(pthread_cond_t *cond);

 Parameters:
• cond: pointer to the condition variable.

 Example:
pthread_mutex_lock(&mutex);
// Change the condition
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

9
4. pthread_cond_broadcast

 Purpose: wake up all threads waiting on a condition variable.


 Syntax:
int pthread_cond_broadcast(pthread_cond_t *cond);

 Parameters:
• cond: pointer to the condition variable.

 Example:
pthread_mutex_lock(&mutex);
// Change the condition
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);

1
5. pthread_cond_destroy

 Purpose: destroy a condition variable, releasing resources.


 Syntax:
int pthread_cond_destroy(pthread_cond_t *cond);

 Parameters:
• cond: pointer to the condition variable.

 Example:
pthread_cond_destroy(&cond);

1
Example 1: Multiple Producers and Consumers

 Problem: Manage multiple producer and consumer threads


accessing a shared buffer. Producers add items to the buffer,
while consumers remove them. The challenge is to synchronize
access and avoid race conditions.

 Solution: Use a mutex to protect the buffer and condition


variables to signal when the buffer is not full (for producers) and
not empty (for consumers). Producers wait if the buffer is full, and
consumers wait if it's empty, ensuring safe and efficient access.

1
Example 1: Multiple Producers and Consumers
Requirements:
void* consumer(void* arg) { consumer wait when
• Producers wait if the buffer is full
while (1) {
empty
• Consumers wait if it's empty pthread_mutex_lock(&mutex);
#include <pthread.h> example while (count == 0) {
#include <stdio.h> pthread_cond_wait(&cond_consume, &mutex);
#include <stdlib.h>
1.c }
#define BUFFER_SIZE 10 int item = buffer[--count]; // Consume item
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; printf("Consumed, count: %d\n", count);
pthread_cond_t cond_produce = PTHREAD_COND_INITIALIZ pthread_cond_signal(&cond_produce);
ER; pthread_mutex_unlock(&mutex);
pthread_cond_t cond_consume = PTHREAD_COND_INITIALIZ } signal producer when not
ER; return NULL;
producer wait when full
int buffer[BUFFER_SIZE]; }
int count = 0; full int main() {
void* producer(void* arg) { pthread_t producers[2], consumers[2];
while (1) { for (int i = 0; i < 2; i++) {
pthread_mutex_lock(&mutex); pthread_create(&producers[i], NULL, producer, N
while (count == BUFFER_SIZE) { ULL);
pthread_cond_wait(&cond_produce, &mutex) pthread_create(&consumers[i], NULL, consumer, N
; ULL);
} }
buffer[count+ for (int i = 0; i < 2; i++) {
+] = rand() % 100; // Produce item
signal consumer when not pthread_join(producers[i], NULL);
printf("Produced, count: %d\n", count); pthread_join(consumers[i], NULL);
empty
pthread_cond_signal(&cond_consume); } 1
pthread_mutex_unlock(&mutex); return 0;
Example 2: Barrier Synchronization

 Problem: In a barrier synchronization scenario, multiple threads


must wait at a specific point until all threads reach that point.
Only when all threads have reached the barrier can they all
proceed.

 Solution: Each thread increases a counter when it reaches the


barrier. If not all threads have arrived, it waits. The last thread to
arrive signals all waiting threads to continue.

1
Example 2: Barrier Synchronization
Requirements:

• Threads wait until all threads reach the point

• Last thread signals all waiting threads


#include <pthread.h> example
#include <stdio.h>
#define NUM_THREADS 5
2.c
int main() {
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_t threads[NUM_THREADS];
pthread_cond_t cond_barrier = PTHREAD_COND_INITIALIZ int thread_ids[NUM_THREADS];
ER; for (int i = 0; i < NUM_THREADS; i++) {
int count = 0; thread_ids[i] = i;
pthread_create(&threads[i], NULL, thread_func,
void* thread_func(void* arg) { &thread_ids[i]);
int id = *(int*)arg; }
printf("Thread %d reached the wait if not all id);
barrier.\n", for (int i = 0; i < NUM_THREADS; i++) {
pthread_mutex_lock(&mutex); pthread_join(threads[i], NULL);
arrive
count++; }
if (count < NUM_THREADS) { return 0;
pthread_cond_wait(&cond_barrier, &mutex); }
} else {
pthread_cond_broadcast(&cond_barrier); the last thread signals all waiting
}
pthread_mutex_unlock(&mutex); threads
printf("Thread %d passed the barrier.\n", id);
return NULL; 1
}
Exercise: Dining Philosophers Problem

 Problem: Five philosophers sit around a table with five forks.


Each philosopher needs two forks to eat but only one is available
on either side. The challenge is to avoid deadlock and ensure all
philosophers eventually eat.

 Solution: Use condition variables to manage fork availability.


Philosophers check if both adjacent forks are available before
picking them up. After eating, they put the forks back and signal
neighbors, allowing them to eat if possible.

Dining philosophers problem - Wikipedia


1
Exercise: Dining Philosophers Problem
#include <pthread.h> exercise. void* philosopher(void* arg) {
#include <stdio.h> int i = *(int*)arg;
#define NUM_PHILOSOPHERS 5
c while (1) {
printf("Philosopher %d is thinking.\n", i);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pickup_forks(i);
pthread_cond_t cond_vars[NUM_PHILOSOPHERS]; printf("Philosopher %d is eating.\n", i);
int state[NUM_PHILOSOPHERS]; return_forks(i);
enum { THINKING, HUNGRY, EATING }; }
return NULL;
void test(int i) { }
// TODO: Implement test function int main() {
// Hint: Check if the philosopher can start eati pthread_t philosophers[NUM_PHILOSOPHERS];
ng int ids[NUM_PHILOSOPHERS];
// by ensuring neighbors are not eating. for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
} pthread_cond_init(&cond_vars[i], NULL);
void pickup_forks(int i) { ids[i] = i;
pthread_mutex_lock(&mutex); pthread_create(&philosophers[i], NULL, philosop
// TODO: Implement pickup_forks function her, &ids[i]);
// Hint: Set state to HUNGRY and then test. }
// Wait if not able to eat. for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
pthread_mutex_unlock(&mutex); pthread_join(philosophers[i], NULL);
} }
void return_forks(int i) { for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
pthread_mutex_lock(&mutex); pthread_cond_destroy(&cond_vars[i]);
// TODO: Implement return_forks function }
// Hint: Set state to THINKING and test neighbor return 0;
s. } 1
General Hints for Using Condition Variables

 Always lock the mutex before using condition variables.

 Use while loops for pthread_cond_wait to handle spurious


wakeups.

 Ensure proper cleanup using pthread_cond_destroy.

1
Q&A

You might also like