为啥这段代码在 valgrind (helgrind) 下失败?
Posted
技术标签:
【中文标题】为啥这段代码在 valgrind (helgrind) 下失败?【英文标题】:Why is this code failing under valgrind (helgrind)?为什么这段代码在 valgrind (helgrind) 下失败? 【发布时间】:2011-04-04 11:41:38 【问题描述】:**已解决:在我的类的构造函数中,我有一个信号量的构造与线程的构造竞争,我希望首先创建信号量,然后创建线程。对我有用的解决方案是首先在基类中创建信号量,这样我就可以在派生类中依赖它。 **
我有一个相当小的 pthreads C++ 程序,它在正常情况下运行良好。然而,当在程序上使用 valgrind 的线程错误检查工具时,它似乎发现了一个竞争条件。使这种竞争条件特别难以避免的原因是它发生在“信号量”类中(它实际上只是封装了 sem_init、sem_wait 和 sem_post),所以我不能用另一个信号量来解决这个问题(而且不应该)。我不认为 valgrind 给出了误报,因为我的程序在 valgrind 下运行时表现出不同的行为。
这里是 Semaphore.cpp * :
#include "信号量.h" #include我已经在其他程序中使用了这个 Semaphore 类,这些程序通过了 helgrind 没有问题,我真的不确定我在这里做了什么特别的事情导致了这个问题。根据 helgrind 的说法,在一个线程中写入 Semaphore 的构造函数和在另一个线程中读取 Semaphore::lock 之间发生了竞争。老实说,我什至不明白这是怎么可能的:一个对象的方法怎么可能与该对象的构造函数有竞争条件? C++ 不保证在可以调用对象上的方法之前已经调用了构造函数吗?即使在多线程环境中,这怎么可能被违反?
不管怎样,现在是 valgrind 输出。我正在使用 valgind 版本“Valgrind-3.6.0.SVN-Debian”。 Memcheck 说一切都很好。这是 hlgrind 的结果:
$ valgrind --tool=helgrind --read-var-info=yes ./try ==7776== Helgrind,一个线程错误检测器 ==7776== 版权所有 (C) 2007-2009 和 GNU GPL,由 OpenWorks LLP 等人提供。 ==7776== 使用 Valgrind-3.6.0.SVN-Debian 和 LibVEX;使用 -h 重新运行以获取版权信息 ==7776== 命令:./try ==7776== 在抛出 '==7776== 线程 #1 是程序的根线程的实例后调用终止 ==7776== ==7776== 线程 #2 已创建 ==7776== 在 0x425FA38: 克隆 (clone.S:111) ==7776== by 0x40430EA: pthread_create@@GLIBC_2.1 (createthread.c:249) ==7776== 由 0x402950C: pthread_create_WRK (hg_intercepts.c:230) ==7776== 由 0x40295A0: pthread_create@* (hg_intercepts.c:257) ==7776== by 0x804CD91: Thread::Thread(void* (*)(void*), void*) (Thread.cpp:10) ==7776== by 0x804B2D5: ActionQueue::ActionQueue() (ActionQueue.h:40) ==7776== by 0x80497CA: main (try.cpp:9) ==7776== ==7776== 线程 #1 在 0x42ee04c 写入大小 4 期间可能出现数据竞争 ==7776== 在 0x804D9C5: 信号量::信号量(bool, int) (Semaphore.cpp:8) ==7776== by 0x804B333: ActionQueue::ActionQueue() (ActionQueue.h:40) ==7776== by 0x80497CA: main (try.cpp:9) ==7776== 这与线程 #2 先前读取的大小 4 冲突 ==7776== 在 0x804D75B: Semaphore::lock() (Semaphore.cpp:26) ==7776== by 0x804B3BE: Lock::Lock(Semaphore&) (Lock.h:17) ==7776== by 0x804B497: ActionQueue::ActionQueueLoop() (ActionQueue.h:56) ==7776== by 0x8049ED5: void* CallMemFun, &(ActionQueue::ActionQueueLoop())>(void*) (CallMemFun.h:7) ==7776== by 0x402961F: mythread_wrapper (hg_intercepts.c:202) ==7776== by 0x404296D: start_thread (pthread_create.c:300) ==7776== by 0x425FA4D: 克隆 (clone.S:130) ==7776== std::runtime_error' 什么():信号量::锁定错误:m_Sem == 0 ==7776== ==7776== 对于检测到和抑制的错误计数,重新运行:-v ==7776== 使用 --history-level=approx 或 =none 来提高速度,在 ==7776== 冲突访问信息准确性降低的代价 ==7776== 错误摘要:1 个上下文中的 1 个错误(抑制:5 个来自 5 个)任何拥有 git 和 valgrind 的人都可以通过查看我的 git repo 分支(据记录,目前正在提交 262369c2d25eb17a0147)中的代码来重现这一点,如下所示:
$ git clone git://github.com/notfed/concqueue -b 信号量 $ cd 继续 $ 制作 $ valgrind --tool=helgrind --read-var-info=yes ./try【问题讨论】:
【参考方案1】:虽然看起来线程在线程 1 完成运行构造函数之前尝试在线程 2 中使用信号量。在这种情况下,可以让 m_Sem 为 NULL(0) 或任何其他值。
【讨论】:
【参考方案2】:好的,我发现了问题。我的 ActionQueue 类在构造时创建(除其他之外)两个对象:信号量和线程。问题是,这个线程正在使用那个信号量。我错误地假设 Semaphore 会在进入构造函数之前自动创建,因为它是一个成员对象。我的解决方案是从构建信号量的基类派生 ActionQueue;这样,当我到达 ActionQueue 的构造函数时,我可以指望基类的成员已经在构造了。
【讨论】:
以上是关于为啥这段代码在 valgrind (helgrind) 下失败?的主要内容,如果未能解决你的问题,请参考以下文章