以下说明如何修复 pthr_prime.c 中的错误。有关完整的文件列表,请参见 2.1.2 pthr_prime.c 的完整列表。
使用单个互斥锁消除 pthr_prime.c 中第 39 行上 total 的读取和第 40 行上 total 的写入之间的数据争用。此添加还修复了 pthr_prime.c 中的两个其他数据争用:第 39 行上 prime[]
的数据争用以及第 40 行上 total 的数据争用。
第 55 行上 i 的写入和第 35 行上 i 的读取之间的数据争用以及第 22 行上 pflag[]
的数据争用显示了不同线程对变量 i 进行共享访问的问题。pthr_prime.c 中的初始线程在循环中创建子线程(源代码的第 55-57 行),并调度它们处理函数 work()。循环索引 i 按地址传递到 work()。由于所有线程都访问 i 的同一内存位置,因此每个线程的 i 值不会保持唯一,而是将随初始线程递增循环索引而改变。由于不同线程使用 i 的同一值,因此发生了数据争用。
修复该问题的一种方法是将 i 按值传递到 work()。这确保每个线程都具有自己的带有唯一值的专用 i 副本。要消除第 39 行 上写入访问和第 65 行上读取访问之间的 primes[]
数据争用,可以使用与前面第 39 行和第 40 行所用相同的互斥锁保护第 65 行。但是,这不是正确的修复方法。真正的问题是,主线程可能会在子线程仍在函数 work() 中更新 total 和 primes[]
的同时报告结果(第 50 行到第 53 行)。使用互斥锁并不提供线程之间的正确排序同步。一种正确的修复方法是在打印出结果之前让主线程等待所有子线程加入。
以下是更正后的 pthr_prime.c 版本:
1 #include <stdio.h> 2 #include <math.h> 3 #include <pthread.h> 4 5 #define THREADS 4 6 #define N 3000 7 8 int primes[N]; 9 int pflag[N]; 10 int total = 0; 11 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 12 13 int is_prime(int v) 14 { 15 int i; 16 int bound = floor(sqrt(v)) + 1; 17 18 for (i = 2; i < bound; i++) { 19 /* no need to check against known composites */ 20 if (!pflag[i]) 21 continue; 22 if (v % i == 0) { 23 pflag[v] = 0; 24 return 0; 25 } 26 } 27 return (v > 1); 28 } 29 30 void *work(void *arg) 31 { 32 int start; 33 int end; 34 int i; 35 36 start = (N/THREADS) * ((int)arg) ; 37 end = start + N/THREADS; 38 for (i = start; i < end; i++) { 39 if ( is_prime(i) ) { 40 pthread_mutex_lock(&mutex); 41 primes[total] = i; 42 total++; 43 pthread_mutex_unlock(&mutex); 44 } 45 } 46 return NULL; 47 } 48 49 int main(int argn, char **argv) 50 { 51 int i; 52 pthread_t tids[THREADS-1]; 53 54 for (i = 0; i < N; i++) { 55 pflag[i] = 1; 56 } 57 58 for (i = 0; i < THREADS-1; i++) { 59 pthread_create(&tids[i], NULL, work, (void *)i); 60 } 61 62 i = THREADS-1; 63 work((void *)i); 64 65 for (i = 0; i < THREADS-1; i++) { 66 pthread_join(tids[i], NULL); 67 } 68 69 printf("Number of prime numbers between 2 and %d: %d\n", 70 N, total); 71 for (i = 0; i < total; i++) { 72 printf("%d\n", primes[i]); 73 } 74 }