ex3.c (4541B)
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 if (pthread_mutex_lock(&f->mutex) != 0) 59 err(1, "pthread_mutex_lock"); 60 tid = f->tid++; 61 if (pthread_mutex_unlock(&f->mutex) != 0) 62 err(1, "pthread_mutex_unlock"); 63 n = f->n / f->ntd; 64 f->sums[tid] = 0; 65 localsum = 0; 66 67 for (i = tid * n; i < (tid + 1) * n; i++) { 68 localsum += f->arr[i] * f->arr[i]; 69 printf("tid: %d\tarr[%d]: %d\n", tid, i, f->arr[i]); 70 } 71 /* 72 * Lock the mutex so that no other thread can write 73 * to f->sums at the same time. 74 */ 75 if (pthread_mutex_lock(&f->mutex) != 0) 76 err(1, "pthread_mutex_lock"); 77 f->sums[tid] = localsum; 78 if (pthread_mutex_unlock(&f->mutex) != 0) 79 err(1, "pthread_mutex_unlock"); 80 81 return NULL; 82 } 83 84 /* Error checking malloc(2) */ 85 static void * 86 emalloc(size_t nb) 87 { 88 void *p; 89 90 if ((p = malloc(nb)) == NULL) 91 err(1, "malloc"); 92 return p; 93 } 94 95 int 96 main(int argc, char *argv[]) 97 { 98 struct foo *f; /* Each callback will receive this */ 99 pthread_t *tds; /* Threads */ 100 int totalsum; /* What its name says */ 101 int rc; /* scanf(3) return value. */ 102 int i; /* Counter */ 103 104 f = emalloc(sizeof(struct foo)); 105 /* 106 * We do error checks for `n` and `ntd` but in case we read from a 107 * file the program might break if the data is wrong (i.e fails the 108 * error checks at least once) since it will keep going further down 109 * into the file. The same doesn't apply for when reading from stdin -- 110 * we can just give it a new number until it's correct. 111 */ 112 do { 113 printf("\rp: "); 114 /* 115 * Keep scanf(3)'s return value to make sure we 116 * did read valid input. 117 */ 118 rc = scanf("%d", &f->ntd); 119 /* Flush input buffer. */ 120 (void)getchar(); 121 /* Cannot have < 0 threads. */ 122 } while (f->ntd < 0 || rc != 1); 123 124 do { 125 printf("\rn: "); 126 scanf("%d", &f->n); 127 (void)getchar(); 128 /* Make sure `n` is positive and also a multiple of `ntd`. */ 129 } while (f->n < 0 || f->n % f->ntd != 0 || rc != 1); 130 131 tds = emalloc(f->ntd * sizeof(pthread_t)); 132 f->arr = emalloc(f->n * sizeof(int)); 133 f->sums = emalloc(f->ntd * sizeof(int)); 134 135 /* Fill the vector. */ 136 srand(time(NULL)); 137 for (i = 0; i < f->n; i++) 138 f->arr[i] = rand() % (ULIM - LLIM) + LLIM; 139 140 if (pthread_mutex_init(&f->mutex, NULL) != 0) 141 err(1, "pthread_mutex_init"); 142 /* 143 * Start multithreading. For each thread we assign `calc` 144 * to be the callback function that each thread will call 145 * and we pass it the `foo` struct as an argument to avoid 146 * declaring globals. 147 */ 148 for (i = 0; i < f->ntd; i++) 149 if (pthread_create(&tds[i], NULL, calc, (void *)f) != 0) 150 err(1, "pthread_create"); 151 for (i = 0; i < f->ntd; i++) 152 if (pthread_join(tds[i], NULL) != 0) 153 err(1, "pthread_join"); 154 totalsum = 0; 155 while (f->ntd--) 156 totalsum += *f->sums++; 157 printf("total sum: %d\n", totalsum); 158 159 (void)pthread_mutex_destroy(&f->mutex); 160 pthread_exit(NULL); 161 free(tds); 162 free(f->arr); 163 free(f->sums); 164 free(f); 165 166 return 0; 167 }