Implementing thread-safe, dynamically resizable arrays in C is indeed a crucial topic for mastering advanced C programming and concurrency control. This knowledge can be essential when dealing with multi-threaded applications where data structures need to be accessed and modified concurrently.
Here's an overview of how you can achieve this:
Thread-Safe Dynamic Arrays in C
- Thread-Safe: Ensuring that multiple threads can access and modify the array without causing data corruption.
- Dynamic Resizing: Allowing the array to grow or shrink in size as needed.
Key Concepts
- Mutex Locks: To ensure that only one thread can modify the array at a time.
- Condition Variables: To manage scenarios where a thread needs to wait until a certain condition is met before proceeding.
Implementation Steps
Define the Dynamic Array Structure
- Include a pointer to the data array.
- Include variables for the current size and capacity.
- Include a mutex for synchronization.
#include <stdlib.h>
#include <pthread.h>
typedef struct {
int *data;
size_t size;
size_t capacity;
pthread_mutex_t lock;
} DynamicArray;
Initialize the Dynamic Array
- Allocate memory for the array.
- Initialize the size and capacity.
- Initialize the mutex.
void initArray(DynamicArray *arr, size_t initialCapacity) {
arr->data = (int *)malloc(initialCapacity * sizeof(int));
arr->size = 0;
arr->capacity = initialCapacity;
pthread_mutex_init(&arr->lock, NULL);
}
Resize the Array
- Double the capacity when the array is full.
- Reallocate memory and copy the old data to the new memory location.
void resizeArray(DynamicArray *arr) {
arr->capacity *= 2;
arr->data = (int *)realloc(arr->data, arr->capacity * sizeof(int));
}
Add Elements to the Array
- Lock the mutex before modifying the array.
- Resize the array if needed.
- Add the new element and unlock the mutex.
void addElement(DynamicArray *arr, int element) {
pthread_mutex_lock(&arr->lock);
if (arr->size == arr->capacity) {
resizeArray(arr);
}
arr->data[arr->size++] = element;
pthread_mutex_unlock(&arr->lock);
}
Remove Elements from the Array
- Lock the mutex before modifying the array.
- Remove the element and optionally resize the array down.
- Unlock the mutex.
void removeElement(DynamicArray *arr) {
pthread_mutex_lock(&arr->lock);
if (arr->size > 0) {
arr->size--;
// Optionally resize down if too much unused capacity
}
pthread_mutex_unlock(&arr->lock);
}
Clean Up
- Free the allocated memory.
- Destroy the mutex.
void freeArray(DynamicArray *arr) {
free(arr->data);
pthread_mutex_destroy(&arr->lock);
}
Example Usage
Here's a simple example of how you might use this dynamic array in a multi-threaded context:
#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 4
#define NUM_ELEMENTS 10
void *threadFunc(void *arg) {
DynamicArray *arr = (DynamicArray *)arg;
for (int i = 0; i < NUM_ELEMENTS; i++) {
addElement(arr, i);
}
return NULL;
}
int main() {
DynamicArray arr;
initArray(&arr, 4);
pthread_t threads[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++) {
pthread_create(&threads[i], NULL, threadFunc, &arr);
}
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
for (size_t i = 0; i < arr.size; i++) {
printf("%d ", arr.data[i]);
}
printf("\n");
freeArray(&arr);
return 0;
}
Conclusion
Implementing a thread-safe, dynamically resizable array in C involves careful handling of memory allocation and synchronization. By using mutexes and condition variables, you can ensure that your data structures are safe for concurrent access and modifications, providing a robust foundation for multi-threaded applications.