线程分析器可以识别由 OpenMP、POSIX 线程和 Oracle Solaris 线程提供的大多数标准同步 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) 手册页和线程分析器用户 API说明了如何使用 API 避免报告此类误报的数据争用。