ここでは、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 の値を使用するので、データの競合が起こります。問題を修正する 1 つの方法は、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 }