Waitgroups in C
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define NUM_WORKERS 5
// This structure will be used to pass data to our threads
typedef struct {
int id;
pthread_mutex_t* mutex;
int* counter;
} thread_data;
// This is the function we'll run in every thread
void* worker(void* arg) {
thread_data* data = (thread_data*)arg;
printf("Worker %d starting\n", data->id);
// Sleep to simulate an expensive task
sleep(1);
printf("Worker %d done\n", data->id);
// Decrement the counter when the thread is done
pthread_mutex_lock(data->mutex);
(*data->counter)--;
pthread_mutex_unlock(data->mutex);
pthread_exit(NULL);
}
int main() {
pthread_t threads[NUM_WORKERS];
thread_data thread_data_array[NUM_WORKERS];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int counter = NUM_WORKERS;
// Launch several threads and initialize their data
for (int i = 0; i < NUM_WORKERS; i++) {
thread_data_array[i].id = i + 1;
thread_data_array[i].mutex = &mutex;
thread_data_array[i].counter = &counter;
int rc = pthread_create(&threads[i], NULL, worker, (void*)&thread_data_array[i]);
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
// Wait for all threads to complete
while (1) {
pthread_mutex_lock(&mutex);
if (counter == 0) {
pthread_mutex_unlock(&mutex);
break;
}
pthread_mutex_unlock(&mutex);
usleep(100000); // Sleep for 100ms to avoid busy waiting
}
pthread_mutex_destroy(&mutex);
return 0;
}
This C program demonstrates the concept of waiting for multiple threads to finish, which is similar to the WaitGroup concept in the original example. Here’s an explanation of the key parts:
We define a
thread_data
structure to pass information to each thread, including an ID, a mutex, and a pointer to a shared counter.The
worker
function simulates work by sleeping for one second, then prints a message when it’s done.In
main
, we create an array ofpthread_t
to hold our threads, and an array ofthread_data
to hold the data for each thread.We use a mutex and a counter to keep track of how many threads are still running. This replaces the WaitGroup from the original example.
We create NUM_WORKERS threads, each running the
worker
function with its ownthread_data
.After creating all threads, we enter a loop that checks the counter. When the counter reaches zero, all threads have finished, and we can exit the program.
We use
usleep
to avoid busy waiting while checking the counter.
To compile and run this program, you would typically do:
$ gcc -o waitgroups waitgroups.c -pthread
$ ./waitgroups
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done
The order of workers starting up and finishing is likely to be different for each invocation.
Note that this approach doesn’t provide a straightforward way to propagate errors from workers. For more advanced use cases in C, you might need to implement additional error handling mechanisms or use more sophisticated threading libraries.