Oracle® Solaris Studio 12.4:线程分析器用户指南

退出打印视图

更新时间: 2014 年 12 月
 
 

修复错误而不是修复数据争用

线程分析器可以帮助查找程序中的数据争用,但是它无法自动查找程序中的错误,也不会建议如何修复找到的数据争用。数据争用可能是由某个错误引入的。这种情况下,必须找到并修复该错误。仅仅消除数据争用并不是正确的做法,这样做可能会使进一步的调试更加困难。

修复 prime_omp.c 中的错误

本节介绍如何修复 prime_omp.c 中的错误。有关所列的完整文件内容,请参见prime_omp.c 的源代码

将第 50 行和第 51 行移动到 critical(临界)段中,以便消除数组 primes[ ] 的元素上的数据争用。

47      #pragma omp parallel for
48      for (i = 2; i < N; i++) {
49          if ( is_prime(i) ) {
                 #pragma omp critical

                 {
50                  primes[total] = i;
51                  total++;
                 }
52          }
53     }

也可以按照如下所示将第 50 行和第 51 行移动到两个 critical(临界)段中,但此更改方式无法更正程序:

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

包含第 50 行和第 51 行的临界段将会消除数据争用,因为线程会使用互斥锁控制它们对 primes[ ] 数组的访问。但是,程序仍是错误的。两个线程可能会使用同一 total 值更新 primes[ ] 的同一元素,而 primes[ ] 的某些元素可能根本不会被赋值。

第二个数据争用(第 23 行中从 pflag[ ] 的读取与第 26 行中对 pflag[ ] 的写入之间的数据争用)实际上是良性争用,因为它并不会导致错误结果。没有必要修复良性数据争用。

修复 prime_pthr.c 中的错误

本节介绍如何修复 prime_pthr.c 中的错误。有关所列的完整文件内容,请参见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  }