下面介绍如何修复 prime_pthr.c 中的错误。有关所列的完整文件内容,请参见2.1.3 prime_pthr.c 的源代码。
使用一个互斥锁消除第 44 行中 prime[ ] 上的数据争用以及第 45 行中 total 上的数据争用。
第 60 行中对 i 的写入与第 40 行中从同一内存位置(名称为 *arg)的读取之间的数据争用,以及第 27 行中 pflag[ ] 上的数据争用,表明不同的线程对变量 i 进行的共享访问存在问题。prime_pthr.c 中的初始线程在第 60-62 行通过循环方式创建子线程,并调度它们处理函数 work()。循环索引 i 按地址传递到 work()。由于所有线程都访问 i 的同一内存位置,因此每个线程的 i 值不会保持唯一,而会随着初始线程对循环索引的递增而改变。由于不同的线程使用同一个 i 值,因此就会发生数据争用。修复该问题的一种方法是按值(而不是按地址)将 i 传递给 work()。
下面是更正后的 prime_pthr.c 版本:
1 /*
2 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All Rights Reserved.
3 * @(#)prime_pthr_fixed.c 1.3 (Oracle) 10/03/26
4 */
5
6 #include <stdio.h>
7 #include <math.h>
8 #include <pthread.h>
9
10 #define THREADS 4
11 #define N 10000
12
13 int primes[N];
14 int pflag[N];
15 int total = 0;
16 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
17
18 int is_prime(int v)
19 {
20 int i;
21 int bound = floor(sqrt(v)) + 1;
22
23 for (i = 2; i < bound; i++) {
24 /* no need to check against known composites */
25 if (!pflag[i])
26 continue;
27 if (v % i == 0) {
28 pflag[v] = 0;
29 return 0;
30 }
31 }
32 return (v > 1);
33 }
34
35 void *work(void *arg)
36 {
37 int start;
38 int end;
39 int i;
40
41 start = (N/THREADS) * ((int)arg) ;
42 end = start + N/THREADS;
43 for (i = start; i < end; i++) {
44 if ( is_prime(i) ) {
45 pthread_mutex_lock(&mutex);
46 primes[total] = i;
47 total++;
48 pthread_mutex_unlock(&mutex);
49 }
50 }
51 return NULL;
52 }
53
54 int main(int argn, char **argv)
55 {
56 int i;
57 pthread_t tids[THREADS-1];
58
59 for (i = 0; i < N; i++) {
60 pflag[i] = 1;
61 }
62
63 for (i = 0; i < THREADS-1; i++) {
64 pthread_create(&tids[i], NULL, work, (void *)i);
65 }
66
67 i = THREADS-1;
68 work((void *)i);
69
70 for (i = 0; i < THREADS-1; i++) {
71 pthread_join(tids[i], NULL);
72 }
73
74 printf("Number of prime numbers between 2 and %d: %d\n",
75 N, total);
76
77 return 0;
78 }