Oracle® Developer Studio 12.5: スレッドアナライザユーザーズガイド

印刷ビューの終了

更新: 2016 年 6 月
 
 

データの競合の原因の診断

このセクションでは、データの競合の原因を診断する基本的な方法について説明します。

データの競合が誤検知であるかどうかをチェックする

誤検知のデータの競合は、スレッドアナライザで報告されるが、実際には起こっていないデータの競合です。つまり、「誤検出」です。スレッドアナライザは、報告する誤検知の数を減らそうと試みます。ただし、ツールが正確なジョブを行えずに、誤検知のデータの競合を報告する場合があります。

誤検知のデータの競合は本当のデータの競合ではなく、したがってプログラムの動作に影響しないので、このデータの競合は無視できます。

誤検知のデータの競合の例については、誤検知を参照してください。レポートから誤検知のデータの競合を削除する方法については、スレッドアナライザユーザー APIを参照してください。

データの競合が影響のないものであるかどうかを確認する

影響のないデータの競合は、存在していてもプログラムの正確さには影響しない意図的なデータの競合です。

一部のマルチスレッドアプリケーションでは、データの競合を引き起こすコードを意図的に使用します。設計によってデータの競合が存在するので、修正は必要ありません。ただし、場合によっては、このようなコードを正しく実行させるには非常に慎重を要します。これらのデータの競合については注意深く調べてください。

影響のない競合については、誤検知を参照してください。

データの競合ではなくバグを修正する

スレッドアナライザは、プログラム内でデータの競合を見つけるときに役立ちますが、プログラム内のバグを自動的に見つけることも、見つかったデータの競合の修正方法を提示することもできません。データの競合は、バグによって生じることもあります。バグを見つけて修正することが重要です。単にデータの競合を取り除くだけでは正しいアプローチにはならず、以降のデバッグがさらに困難になる可能性があります。

prime_omp.c でのバグの修正

このセクションでは、prime_omp.c でのバグを修正する方法について説明します。完全なファイルのリストについては、「prime_omp.c のソースコード」を参照してください。

配列 primes[ ] の要素でのデータの競合を削除するために、行 49 および 50 を critical セクションに移します。

46      #pragma omp parallel for
47      for (i = 2; i < N; i++) {
48        if ( is_prime(i) ) {
             #pragma omp critical              
            {    
49             primes[total] = i;
50             total++;
                }
51           }
52      }

また、次のように行 49 および 50 を 2 つの critical セクションに移すこともできますが、この変更ではプログラムを修正できません。

46      #pragma omp parallel fo
47      for (i = 2; i < N; i++) {
48          if ( is_prime (i) ) {
                 #pragma omp critical
                 {
49                  primes [total] = i;
                 }
                 #pragma omp critical
                 {
50                  total++;
                 }
51          }
52     }

スレッドは、排他的ロックを使用して primes[ ] 配列へのアクセスを制御しているので、行 49 および 50 の critical セクションによってデータの競合が取り除かれます。ただし、プログラムはまだ正しくありません。2 つのスレッドは、同じ total 値を使用して primes[ ] の同じ要素を更新する可能性があり、primes[ ] の要素の中には、値がまったく割り当てられないものが生じる可能性があります。

行 22 での pflag[ ] からの読み取りと、行 25 での pflag[ ] への書き込みとの 2 番目のデータの競合は間違った結果を招かないので、実際には影響のない競合です。影響のないデータの競合の修正は必須ではありません。

prime_pthr.c でのバグの修正

このセクションでは、prime_pthr.c でのバグを修正する方法について説明します。完全なファイルのリストについては、prime_pthr.c のソースコードを参照してください。

行 50 での prime[ ] のデータ競合と行 44 での total のデータ競合を取り除くには、これら 2 つの行の前後に相互排他ロック/ロック解除を追加することで、一度に prime[ ] や total を更新できるスレッドが 1 つだけになるようにします。

行 59 での i への書き込みと、行 39 での (*arg という) 同じメモリー位置の読み取りとのデータの競合では、別々のスレッドによる変数 i への共有アクセスに問題があることがわかります。prime_pthr.c の初期スレッドは、行 59-61 でループの子スレッドを作成し、関数 work() を実行するようにこれらをディスパッチします。ループ インデックス i は、アドレスで work() に渡されます。すべてのスレッドは i に対して同じメモリー位置にアクセスするので、各スレッドの i の値は一意のままではありませんが、初期スレッドがループ インデックスを増やすたびに変化します。別々のスレッドが同じ i の値を使用するので、データの競合が起こります。問題を修正する 1 つの方法は、i をアドレスではなく値で work() に渡すことです。

次のコードは、修正されたバージョンの prime_pthr.c です。

     1	/* 
     2	 * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All Rights Reserved.
     3	 */
     4	
     5	#include <stdio.h>
     6	#include <math.h>
     7	#include <pthread.h>
     8	
     9	#define THREADS 4
    10	#define N 10000
    11	
    12	int primes[N];
    13	int pflag[N];
    14	int total = 0;
    15	pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    16	
    17	int is_prime(int v)
    18	{
    19	    int i;
    20	    int bound = floor(sqrt(v)) + 1;
    21	
    22	    for (i = 2; i < bound; i++) {
    23	        /* no need to check against known composites */ 
    24	        if (!pflag[i])
    25	            continue;
    26	        if (v % i == 0) {
    27	            pflag[v] = 0;
    28	            return 0;
    29	        }
    30	    }
    31	    return (v > 1); 
    32	}
    33	
    34	void * work(void *arg)
    35	{
    36	    int start;
    37	    int end;
    38	    int i;
    39	    
    40	    start = (N/THREADS) * ((int)arg) ;
    41	    end = start + N/THREADS;
    42	    for (i = start; i < end; i++) {
    43	        if ( is_prime(i) ) {
    44	            pthread_mutex_lock(&mutex);
    45	            primes[total] = i;
    46	            total++;        
    47	            pthread_mutex_unlock(&mutex);
    48	        }
    49	    }
    50	    return NULL;
    51	}
    52	
    53	int main(int argn, char **argv)
    54	{
    55	    int i;
    56	    pthread_t tids[THREADS-1];
    57	
    58	    for (i = 0; i < N; i++) {
    59	        pflag[i] = 1; 
    60	    }
    61	
    62	    for (i = 0; i < THREADS-1; i++) {
    63	        pthread_create(&tids[i], NULL, work, (void *)i);
    64	    }
    65	
    66	    i = THREADS-1;
    67	    work((void *)i);
    68	    
    69	    for (i = 0; i < THREADS-1; i++) {
    70	        pthread_join(tids[i], NULL);
    71	    }
    72	
    73	    printf("Number of prime numbers between 2 and %d: %d\n",
    74	           N, total);
    75	
    76	    return 0;
    77	}