Assignment-04-A
Title
Thread synchronization using counting semaphores. Application to demonstrate: producer-consumer problem with counting semaphores and mutex.
Theory
Counting semaphores are a synchronization primitive used to control access to a resource that has multiple instances available. In the context of the producer-consumer problem, counting semaphores can be employed to ensure that the producer does not produce more items than the available space in the buffer, and the consumer does not consume items from an empty buffer.
Code Implementation
producer_consumer.c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define BUFFER_SIZE 5
#define PRODUCER_THREADS 3
#define CONSUMER_THREADS 2
#define MAX_ITEMS 10
sem_t empty, full;
pthread_mutex_t mutex;
int buffer[BUFFER_SIZE];
int in = 0, out = 0, item = 0;
void *producer(void *arg) {
int id = *((int *) arg);
while (1) {
sleep(rand() % 3); // Simulate production time
sem_wait(&empty);
pthread_mutex_lock(&mutex);
buffer[in] = item++;
printf("Producer %d produced item %d\n", id, buffer[in]);
in = (in + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
pthread_exit(NULL);
}
void *consumer(void *arg) {
int id = *((int *) arg);
while (1) {
sleep(rand() % 3); // Simulate consumption time
sem_wait(&full);
pthread_mutex_lock(&mutex);
int consumed_item = buffer[out];
printf("Consumer %d consumed item %d\n", id, consumed_item);
out = (out + 1) % BUFFER_SIZE;
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
pthread_exit(NULL);
}
int main() {
pthread_t producer_threads[PRODUCER_THREADS], consumer_threads[CONSUMER_THREADS];
int producer_ids[PRODUCER_THREADS], consumer_ids[CONSUMER_THREADS];
// Initialize semaphores and mutex
sem_init(&empty, 0, BUFFER_SIZE);
sem_init(&full, 0, 0);
pthread_mutex_init(&mutex, NULL);
// Create producer threads
for (int i = 0; i < PRODUCER_THREADS; i++) {
producer_ids[i] = i + 1;
pthread_create(&producer_threads[i], NULL, producer, &producer_ids[i]);
}
// Create consumer threads
for (int i = 0; i < CONSUMER_THREADS; i++) {
consumer_ids[i] = i + 1;
pthread_create(&consumer_threads[i], NULL, consumer, &consumer_ids[i]);
}
// Join threads
for (int i = 0; i < PRODUCER_THREADS; i++) {
pthread_join(producer_threads[i], NULL);
}
for (int i = 0; i < CONSUMER_THREADS; i++) {
pthread_join(consumer_threads[i], NULL);
}
// Destroy semaphores and mutex
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&mutex);
return 0;
}
Setup and Run
- Save the code as
producer_consumer.c
. - Compile the program using any C compiler (e.g., gcc).
- Run the compiled executable file.
- Observe the producer and consumer threads producing and consuming items respectively.
Example
gcc -o producer_consumer producer_consumer.c -lpthread
./producer_consumer
Explanation
- The program creates multiple producer and consumer threads that produce and consume items respectively from a shared buffer.
- Counting semaphores (
empty
andfull
) are used to control the number of empty and full slots in the buffer, ensuring that producers do not produce more items than available space and consumers do not consume items from an empty buffer. - A mutex (
mutex
) is employed to protect critical sections of code where multiple threads may access shared variables concurrently, preventing race conditions. - Each producer thread produces items with random time intervals, and each consumer thread consumes items with random time intervals, demonstrating the synchronization of multiple threads using counting semaphores and mutex.
Contact
For any questions or clarifications, please contact Parth Sali at parthsali04@gmail.com.