JavaScript is required to for searching.
跳过导航链接
退出打印视图
Oracle Solaris Studio 12.3:线程分析器用户指南     Oracle Solaris Studio 12.3 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  什么是线程分析器?它有什么作用?

2.  数据争用教程

2.1 数据争用教程源文件

2.1.1 获取数据争用教程源文件

2.1.2 prime_omp.c 的源代码

2.1.3 prime_pthr.c 的源代码

2.1.3.1 数据争用在 prime_omp.cprime_pthr.c 中的效果

2.2 如何使用线程分析器查找数据争用

2.2.1 检测代码

2.2.1.1 检测源代码

2.2.1.2 检测二进制代码

2.2.2 创建数据争用检测实验

2.2.3 检查数据争用检测实验

2.2.3.1 使用线程分析器查看数据争用实验

2.2.3.2 使用 er_print 查看数据争用实验

2.3 了解实验结果

2.3.1 prime_omp.c 中的数据争用

2.3.2 prime_pthr.c 中的数据争用

2.3.3 数据争用的调用堆栈跟踪

2.4 诊断数据争用的原因

2.4.1 检查数据争用是否为误报

2.4.2 检查数据争用是否为良性

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

2.4.3.1 修复 prime_omp.c 中的错误

2.4.3.2 修复 prime_pthr.c 中的错误

2.5 误报

2.5.1 用户定义的同步

2.5.2 由不同线程回收的内存

2.6 良性数据争用

2.6.1 用于查找质数的程序

2.6.2 用于检验数组值类型的程序

2.6.3 使用双检锁的程序

3.  死锁教程

A.  线程分析器可识别的 API

B.  有用提示

2.4 诊断数据争用的原因

本节介绍诊断数据争用原因的基本策略。

2.4.1 检查数据争用是否为误报

误报数据争用是指线程分析器已报告但实际并未发生的数据争用。线程分析器会尽可能减少误报数量,但在某些情况下,该工具无法完成精确的工作并可能误报数据争用。

由于误报的数据争用不是真正的数据争用,并不会影响程序的行为,因此可将其忽略。

有关误报数据争用的一些示例,请参见2.5 误报。有关如何从报告中消除误报数据争用的信息,请参见A.1 线程分析器用户 API

2.4.2 检查数据争用是否为良性

良性数据争用是指特意允许的数据争用,其存在不会影响程序正确性。

一些多线程应用程序会特意使用可能导致数据争用的代码。由于这些数据争用是专门设计的,所以不需要进行修复。但在某些情况下,要使这样的代码正确运行会相当棘手。应仔细检查这些数据争用。

有关良性争用的更多详细信息,请参见2.5 误报

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

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

2.4.3.1 修复 prime_omp.c 中的错误

下面介绍如何修复 prime_omp.c 中的错误。有关所列的完整文件内容,请参见2.1.2 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[ ] 的写入之间的数据争用)实际上是良性争用,因为它并不会导致错误结果。没有必要修复良性数据争用。

2.4.3.2 修复 prime_pthr.c 中的错误

下面介绍如何修复 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  }