Welcome to a deep dive into C Programming! Today, let's explore Read-Write Locks, an essential concept for managing concurrent access to shared data. š”
Read-Write Locks (RW Locks) are synchronization primitives that allow multiple read operations to be performed concurrently while preventing write operations. This is crucial for enhancing the performance of multi-threaded programs by reducing contention.
Imagine a library where multiple readers can access a book simultaneously but only one person can borrow or return a book at a time. Read-Write Locks mimic this scenario in the world of programming.
C doesn't provide a built-in Read-Write Lock mechanism. However, we can create our own using mutexes and condition variables.
Here's a simple implementation:
#include <pthread.h>
#include <stdbool.h>
typedef struct {
pthread_mutex_t rw_mutex;
pthread_cond_t readers;
pthread_cond_t writer;
bool writing;
} rwlock_t;
void rwlock_init(rwlock_t *lock) {
pthread_mutex_init(&lock->rw_mutex, NULL);
pthread_cond_init(&lock->readers, NULL);
pthread_cond_init(&lock->writer, NULL);
lock->writing = false;
}
void rwlock_wrlock(rwlock_t *lock) {
pthread_mutex_lock(&lock->rw_mutex);
while (lock->writing) {
pthread_cond_wait(&lock->writer, &lock->rw_mutex);
}
lock->writing = true;
}
void rwlock_rdlock(rwlock_t *lock) {
pthread_mutex_lock(&lock->rw_mutex);
while (lock->writing) {
pthread_cond_wait(&lock->readers, &lock->rw_mutex);
}
}
void rwlock_unlock(rwlock_t *lock) {
pthread_mutex_unlock(&lock->rw_mutex);
pthread_cond_broadcast(&lock->readers);
lock->writing = false;
}š Note: This implementation is basic and may not be suitable for high-load applications. For such cases, consider using more robust solutions like Google's RWLock.
Now let's use our Read-Write Locks to manage a shared data structure, like a queue, in a multi-threaded environment.
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
#define NUM_THREADS 10
#define QUEUE_SIZE 5
typedef struct {
int items[QUEUE_SIZE];
int head, tail;
rwlock_t rw_lock;
} queue_t;
queue_t queue;
void enqueue(int value) {
rwlock_rdlock(&queue.rw_lock);
if ((queue.tail + 1) % QUEUE_SIZE == queue.head) {
rwlock_unlock(&queue.rw_lock);
printf("Queue is full. Dropping item %d\n", value);
return;
}
queue.items[queue.tail] = value;
queue.tail = (queue.tail + 1) % QUEUE_SIZE;
rwlock_unlock(&queue.rw_lock);
rwlock_wrlock(&queue.rw_lock);
printf("Enqueued item %d\n", value);
rwlock_unlock(&queue.rw_lock);
}
int dequeue() {
int value;
rwlock_wrlock(&queue.rw_lock);
if (queue.head == queue.tail) {
rwlock_unlock(&queue.rw_lock);
return -1;
}
value = queue.items[queue.head];
queue.head = (queue.head + 1) % QUEUE_SIZE;
rwlock_unlock(&queue.rw_lock);
rwlock_rdlock(&queue.rw_lock);
printf("Dequeued item %d\n", value);
rwlock_unlock(&queue.rw_lock);
return value;
}
void *producer(void *arg) {
for (int i = 0; i < 10; i++) {
enqueue(i);
sleep(1);
}
return NULL;
}
void *consumer(void *arg) {
while (true) {
int value = dequeue();
if (value == -1) break;
printf("Consumed item %d\n", value);
sleep(2);
}
return NULL;
}
int main() {
rwlock_init(&queue.rw_lock);
queue.head = queue.tail = 0;
pthread_t producers[NUM_THREADS], consumers[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&producers[i], NULL, producer, NULL);
pthread_create(&consumers[i], NULL, consumer, NULL);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(producers[i], NULL);
pthread_join(consumers[i], NULL);
}
rwlock_destroy(&queue.rw_lock);
return 0;
}What is the purpose of Read-Write Locks in C Programming?