ex3.c (4204B)
1 #include <err.h> 2 #include <pthread.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <time.h> 6 #include <unistd.h> 7 8 /* 9 * Εργαστήριο ΛΣ2 (Δ6) / Εργασία 1: Άσκηση 3 / 2020-2021 10 * Ονοματεπώνυμο: Χρήστος Μαργιώλης 11 * ΑΜ: 19390133 12 * Τρόπος μεταγλώττισης: `cc ex3.c -lpthread -DLLIM=x -DULIM=y -o ex3` 13 * Όπου `x` και `y` το κάτω και πάνω όριο αντίστοιχα. 14 */ 15 16 /* No globals allowed :-) */ 17 struct foo { 18 int *sums; /* Sums for each thread */ 19 int *arr; /* Global vector */ 20 int n; /* `arr`'s length */ 21 int ntd; /* Number of threads */ 22 int tid; /* Thread ID */ 23 pthread_mutex_t mutex; /* Protect critical parts */ 24 }; 25 26 /* Function declarations */ 27 static void *calc(void *); 28 static void *emalloc(size_t); 29 30 /* 31 * Each thread calculates the sum of the squares of each element in a specified 32 * region in `arr`. That range is determined by the thread's ID (e.g the current 33 * thread) and the number of elements that each thread has to compute. Since 34 * `arr`'s length is a multiple of `ntd`, we can calculate the number of elements 35 * each thread will compute by doing `n / ntd`. 36 * 37 * For example, `ntd = 2` and `n = 4`, and `arr = [1, 2, 3, 4]`. 38 * Each thread will compute `n / ntd = 2` elements. 39 * 40 * Thread 0 will operate in the region: 41 * `td * (n / ntd) = 0 * 2 = 0` to 42 * `(td + 1) * (n / ntd) - 1 = (0 + 1) * 2 - 1 = 1` 43 * 44 * Thread 1 will operate in the region: 45 * `1 * 2 = 2` to `(1 + 1) * 2 - 1 = 3` 46 * 47 * So effectively, each thread will be assigned to compute an equal amount of 48 * elements. 49 */ 50 static void * 51 calc(void *foo) 52 { 53 struct foo *f; 54 int localsum; 55 int tid, n, i; 56 57 f = (struct foo *)foo; 58 tid = f->tid; 59 n = f->n / f->ntd; 60 f->sums[tid] = 0; 61 localsum = 0; 62 63 for (i = tid * n; i < (tid + 1) * n; i++) { 64 localsum += f->arr[i] * f->arr[i]; 65 printf("tid: %d\tarr[%d]: %d\n", tid, i, f->arr[i]); 66 } 67 /* 68 * Lock the mutex so that no other thread can write 69 * to f->sums at the same time. 70 */ 71 if (pthread_mutex_lock(&f->mutex) != 0) 72 err(1, "pthread_mutex_lock"); 73 f->sums[tid] = localsum; 74 if (pthread_mutex_unlock(&f->mutex) != 0) 75 err(1, "pthread_mutex_unlock"); 76 77 return NULL; 78 } 79 80 /* Error checking malloc(2) */ 81 static void * 82 emalloc(size_t nb) 83 { 84 void *p; 85 86 if ((p = malloc(nb)) == NULL) 87 err(1, "malloc"); 88 return p; 89 } 90 91 int 92 main(int argc, char *argv[]) 93 { 94 struct foo *f; /* Each callback will receive this */ 95 pthread_t *tds; /* Threads */ 96 pthread_t fin; /* Will calculate the total sum */ 97 int totalsum; /* What its name says */ 98 int i; /* Counter */ 99 100 f = emalloc(sizeof(struct foo)); 101 /* 102 * We do error checks for `n` and `ntd` but in case we read from a 103 * file the program might break if the data is wrong (i.e fails the 104 * error checks at least once) since it will keep going further down 105 * into the file. The same doesn't apply for when reading from stdin -- 106 * we can just give it a new number until it's correct. 107 */ 108 do { 109 printf("p: "); 110 scanf("%d", &f->ntd); 111 /* Cannot have < 0 threads. */ 112 } while (f->ntd < 0); 113 114 do { 115 printf("n: "); 116 scanf("%d", &f->n); 117 /* Make sure `n` is positive and also a multiple of `ntd`. */ 118 } while (f->n < 0 || f->n % f->ntd != 0); 119 120 tds = emalloc(f->ntd * sizeof(pthread_t)); 121 f->arr = emalloc(f->n * sizeof(int)); 122 f->sums = emalloc(f->ntd * sizeof(int)); 123 124 /* Fill the vector. */ 125 srand(time(NULL)); 126 for (i = 0; i < f->n; i++) 127 f->arr[i] = rand() % (ULIM - LLIM) + LLIM; 128 129 if (pthread_mutex_init(&f->mutex, NULL) != 0) 130 err(1, "pthread_mutex_init"); 131 /* 132 * Start multithreading. For each thread we assign `calc` 133 * to be the callback function that each thread will call 134 * and we pass it the `foo` struct as an argument to avoid 135 * declaring globals. 136 */ 137 for (i = 0; i < f->ntd; i++) { 138 f->tid = i; 139 if (pthread_create(&tds[i], NULL, calc, (void *)f) != 0) 140 err(1, "pthread_create"); 141 if (pthread_join(tds[i], NULL) != 0) 142 err(1, "pthread_join"); 143 } 144 totalsum = 0; 145 while (f->ntd--) 146 totalsum += *f->sums++; 147 printf("total sum: %d\n", totalsum); 148 149 (void)pthread_mutex_destroy(&f->mutex); 150 pthread_exit(NULL); 151 free(tds); 152 free(f->arr); 153 free(f->sums); 154 free(f); 155 156 return 0; 157 }