D Multi-Threading in C
D.1 The AD Server that loses Money
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int ghc = 0;
int num_threads = 0;
struct server_thread_info {
;
pthread_t t_idint lhc;
};
static void* increment_hc(void *t){
struct server_thread_info* t_server = t;
->lhc = 0;
t_server
int hits = rand()%1000000 + 100000;
for(int i = 0; i < hits; i++){
->lhc++;
t_server++;
ghc}
("Thread %d incremented the global hit counter %d times.\n", t_server->t_id, t_server->lhc);
printfreturn t;
}
int main(void) {
int s;
("Number of threads for the ad server? ");
printf//Get number of threads from stdin
("%d", &num_threads);
scanf
//Create thread information data structure
struct server_thread_info *t_info;
= calloc(num_threads, sizeof(struct server_thread_info));
t_info if(t_info == NULL) handle_error("calloc");
//Create and run threads
for(int t=0; t<num_threads; t++){
//Initialize local hit counter to 0.
[t].lhc = 0;
t_info
// Each thread calls increment_hc() on startup
= pthread_create(&t_info[t].t_id, NULL, &increment_hc, &t_info[t]);
s if(s!= 0)handle_error_en(s, "pthread_create");
}
//Cleanup all threads and check hit counter count
int real_ghc = 0;
for(int t=0;t<num_threads; t++){
= pthread_join(t_info[t].t_id, NULL);
s if(s!=0) handle_error_en(s, "pthread_join");
+= t_info[t].lhc;
real_ghc
}
("The global hit counter is %d and it should be %d\n", ghc, real_ghc);
printf("You lost %f Dirhams!\n", (real_ghc - ghc) / 100.0 / 100.0);
printfreturn EXIT_SUCCESS;
}
Now that you see the issues with lack of sychronization. Introduce spin locks to fix the problem:
First create a global lock for access to the global hit counter:
#include <stdatomic.h>
= ATOMIC_FLAG_INIT; atomic_flag ghc_busy
Then, identify the critical section and place the locks around it.
static void* increment_hc(void *t){
struct server_thread_info* t_server = t;
->lhc = 0;
t_server
int hits = rand()%1000000 + 100000;
for(int i = 0; i < hits; i++){
->lhc++;
t_serverwhile(atomic_flag_test_and_set(&ghc_busy));//Spin - Note the ; at the end
++; //Update counter
ghc(&ghc_busy);//Release lock
atomic_flag_clear}
("Thread %d incremented the global hit counter %d times.\n",
printf->t_id, t_server->lhc);
t_serverreturn t;
}
Now replace the spin lock with a semaphore/mutex instead!