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.5 误报

有时,线程分析器可能会报告实际在程序中并未发生的数据争用。我们将这些称为误报。大多数情况下,误报是由用户定义的同步导致的,或由不同线程回收的内存导致的。有关更多信息,请参见2.5.1 用户定义的同步2.5.2 由不同线程回收的内存

2.5.1 用户定义的同步

线程分析器可以识别由 OpenMP、POSIX 线程和 Solaris 线程提供的大多数标准同步 API 和构造。但是,该工具无法识别用户定义的同步,如果代码中包含这样的同步,可能会误报数据争用。


注 - 为了避免报告此类误报的数据争用,线程分析器提供了一组 API,可用于在执行用户定义的同步时通知该工具。有关更多信息,请参见A.1 线程分析器用户 API


为了说明为何需要使用这些 API,请考虑以下内容。线程分析器无法使用 CAS 指令来识别锁的实现,也无法使用忙等待来识别发布和等待操作,等等。下面是一类误报的典型示例,其中程序利用的是 POSIX 线程条件变量的常见用法:

/* Initially ready_flag is 0 */
 
/* Thread 1: Producer */
100   data = ...
101   pthread_mutex_lock (&mutex);  
102   ready_flag = 1;
103   pthread_cond_signal (&cond);
104   pthread_mutex_unlock (&mutex);
...
/* Thread 2: Consumer */
200   pthread_mutex_lock (&mutex);
201   while (!ready_flag) {
202       pthread_cond_wait (&cond, &mutex);   
203   }
204   pthread_mutex_unlock (&mutex);
205   ... = data;

pthread_cond_wait() 调用通常在循环内进行,该循环的作用是对谓词进行测试以防止发生程序错误和虚假唤醒。谓词的测试和设置通常由互斥锁加以保护。在以上代码中,线程 1 在第 100 行生成变量 data 的值,在第 102 行将 ready_flag 的值设置为 1 以表明已生成该数据,然后调用 pthread_cond_signal() 以唤醒使用方线程(即线程 2)。线程 2 在循环内测试谓词 (!ready_flag)。当它发现已设置该标志时,将在第 205 行使用数据。

第 102 行 ready_flag 的写入和第 201 行 ready_flag 的读取由同一互斥锁加以保护,因此这两次访问之间不存在数据争用,并且该工具可以正确识别该情况。

第 100 行 data 的写入和第 205 行 data 的读取不受互斥锁的保护。但是,在程序逻辑中,第 205 行的读取总是发生在第 100 行的写入之后,原因是存在标志变量 ready_flag。因此,这两次数据访问之间不存在数据争用。但是,如果在运行时未实际对 pthread_cond_wait() 进行调用(第 202 行),该工具将报告这两次访问之间存在数据争用。如果在执行第 201 行之前执行了第 102 行,则在执行第 201 行时,循环条目测试就会失败,因此将跳过第 202 行。该工具会监视 pthread_cond_signal() 调用和 pthread_cond_wait() 调用,并可使它们成对以形成同步。如果未调用第 202 行的 pthread_cond_wait(),该工具并不知道第 100 行的写入总是在第 205 行的读取之前执行。因此,该工具认为它们是同时执行的,并报告它们之间的数据争用。

libtha(3C) 手册页和A.1 线程分析器用户 API说明了如何使用 API 避免报告此类误报的数据争用。

2.5.2 由不同线程回收的内存

有些内存管理例程负责回收由一个线程释放的内存,以供另一个线程使用。由不同线程使用的同一内存位置的使用期限不会重叠,线程分析器有时无法认识这一点。发生这种情况时,该工具可能会误报数据争用。以下示例将说明此类误报。

/*----------*/                    /*----------*/
/* Thread 1 */                    /* Thread 2 */
/*----------*/                    /*----------*/
 ptr1 = mymalloc(sizeof(data_t));
 ptr1->data = ...
 ...
 myfree(ptr1);

                                  ptr2 = mymalloc(sizeof(data_t));
                                  ptr2->data = ...
                                  ...
                                  myfree(ptr2);

线程 1 和线程 2 同时执行。每个线程都会分配一个用作专用内存的内存块。mymalloc() 例程可能会提供先前由 myfree() 调用所释放的内存。如果线程 2 在线程 1 调用 myfree() 之前调用 mymalloc(),则 ptr1ptr2 会获得不同的值,并且在这两个线程之间不存在数据争用。但是,如果线程 2 在线程 1 调用 myfree() 之后调用 mymalloc(),则 ptr1ptr2 可能会具有相同的值。由于线程 1 不再访问该内存,所以不存在数据争用。但是,如果该工具不知道 mymalloc() 正在回收内存,它会报告 ptr1 数据的写入与 ptr2 数据的写入之间存在数据争用。在 C++ 应用程序中,当 C++ 运行时库回收临时变量的内存时,经常发生此类误报。在实现自己的内存管理例程的用户应用程序中,也经常发生此类误报。目前,线程分析器能够识别通过标准 malloc()calloc()realloc() 接口执行的内存分配和释放操作。