跳过导航链接 | |
退出打印视图 | |
Oracle Solaris Studio 12.3:线程分析器用户指南 Oracle Solaris Studio 12.3 Information Library (简体中文) |
本节介绍诊断数据争用原因的基本策略。
误报数据争用是指线程分析器已报告但实际并未发生的数据争用。线程分析器会尽可能减少误报数量,但在某些情况下,该工具无法完成精确的工作并可能误报数据争用。
由于误报的数据争用不是真正的数据争用,并不会影响程序的行为,因此可将其忽略。
有关误报数据争用的一些示例,请参见2.5 误报。有关如何从报告中消除误报数据争用的信息,请参见A.1 线程分析器用户 API。
良性数据争用是指特意允许的数据争用,其存在不会影响程序正确性。
一些多线程应用程序会特意使用可能导致数据争用的代码。由于这些数据争用是专门设计的,所以不需要进行修复。但在某些情况下,要使这样的代码正确运行会相当棘手。应仔细检查这些数据争用。
有关良性争用的更多详细信息,请参见2.5 误报。
线程分析器可以帮助查找程序中的数据争用,但是它无法自动查找程序中的错误,也不会建议如何修复找到的数据争用。数据争用可能是由某个错误引入的。这种情况下,必须找到并修复该错误。仅仅消除数据争用并不是正确的做法,这样做可能会使进一步的调试更加困难。
下面介绍如何修复 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[ ] 的写入之间的数据争用)实际上是良性争用,因为它并不会导致错误结果。没有必要修复良性数据争用。
下面介绍如何修复 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 }