C++ 为什么valgrind(helgrind)会产生;“可能的数据竞争”;如果在我的线程结构上调用虚拟函数
当我开始学习valgrind(helgrind)工具时,我遇到了一个如此棘手的问题,以至于我未能解决 简单地说,用户定义的线程类是用一个虚拟函数创建的,该函数将由线程的入口例程调用。如果是这种情况,helgrind将报告可能的数据竞争。但在简单地省略了virtual关键字之后,就不会报告这样的错误了。 为什么会这样?我的代码有问题吗?还是有解决办法 下面是演示此类问题的简单线程应用程序,包括cpp、Makefile和helgrind报告的消息C++ 为什么valgrind(helgrind)会产生;“可能的数据竞争”;如果在我的线程结构上调用虚拟函数,c++,valgrind,C++,Valgrind,当我开始学习valgrind(helgrind)工具时,我遇到了一个如此棘手的问题,以至于我未能解决 简单地说,用户定义的线程类是用一个虚拟函数创建的,该函数将由线程的入口例程调用。如果是这种情况,helgrind将报告可能的数据竞争。但在简单地省略了virtual关键字之后,就不会报告这样的错误了。 为什么会这样?我的代码有问题吗?还是有解决办法 下面是演示此类问题的简单线程应用程序,包括cpp、Makefile和helgrind报告的消息 /* main.cpp */ #include &l
/* main.cpp */
#include <memory.h>
#include <pthread.h>
class thread_s {
public:
pthread_t th;
thread_s(void);
~thread_s(void);
virtual void* routine(); /* if omit virtual, no error would be generated */
void stop(void);
};
static void* routine(void*);
int main(int, const char*[])
{
thread_s s_v;
pthread_create(&s_v.th, 0, routine, &s_v);
return 0;
}
static void* routine(void* arg)
{
thread_s *pV = reinterpret_cast<thread_s*>(arg);
pV->routine();
return 0;
}
void* thread_s::routine(void)
{
return 0;
}
thread_s::thread_s(void)
{
th = 0;
}
thread_s::~thread_s(void)
{
stop();
}
void thread_s::stop(void)
{
void *v = 0;
pthread_join(th, &v);
}
=======================================
/* Makefile */
all: main test_helgrind
main: main.cpp
g++ -o main main.cpp \
-g -Wall -O0 \
-lpthread
test_helgrind:
valgrind \
--tool=helgrind \
./main
clean:
rm -f main
.PHONY: clean
g++ -o main main.cpp \
-g -Wall -O0 \
-lpthread
valgrind \
--tool=helgrind \
./main
==7477== Helgrind, a thread error detector
==7477== Copyright (C) 2007-2010, and GNU GPL'd, by OpenWorks LLP et al.
==7477== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==7477== Command: ./main
==7477==
==7477== Thread #1 is the program's root thread
==7477==
==7477== Thread #2 was created
==7477== at 0x4259728: clone (clone.S:111)
==7477== by 0x40484B5: pthread_create@@GLIBC_2.1 (createthread.c:256)
==7477== by 0x4026E2D: pthread_create_WRK (hg_intercepts.c:257)
==7477== by 0x4026F8B: pthread_create@* (hg_intercepts.c:288)
==7477== by 0x8048560: main (main.cpp:18)
==7477==
==7477== Possible data race during write of size 4 at 0xbeab24c8 by thread #1
==7477== at 0x80485C9: thread_s::~thread_s() (main.cpp:35)
==7477== by 0x8048571: main (main.cpp:17)
==7477== This conflicts with a previous read of size 4 by thread #2
==7477== at 0x804858B: routine(void*) (main.cpp:24)
==7477== by 0x4026F60: mythread_wrapper (hg_intercepts.c:221)
==7477== by 0x4047E98: start_thread (pthread_create.c:304)
==7477== by 0x425973D: clone (clone.S:130)
==7477==
==7477==
==7477== For counts of detected and suppressed errors, rerun with: -v
==7477== Use --history-level=approx or =none to gain increased speed, at
==7477== the cost of reduced accuracy of conflicting-access information
==7477== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 1 from 1)
一种情况下写入vptr,另一种情况下读取vptr。两者都没有锁。Helgrind无法知道程序中是否有其他方法使这种情况不可能在两个线程中同时发生,因此它会标记它。如果您可以保证在另一个线程中有人试图调用该对象上的函数时该对象不会被破坏,那么您可以对此生成抑制。我不知道这是否是helgrind抱怨的原因,但您的程序中存在严重问题。创建一个线程,将指针传递给本地
线程实例(main()
)中的s\u v
)
但是,main()
将很快返回,而不会与线程进行任何类型的同步-当线程函数routine()
抓住指针并使用它调用pV->routine()
时,无法确保suv
仍处于活动状态
查看在调用pthread\u create()
后添加以下内容是否可以防止helgrind抱怨:
pthread_join( s_v.th, NULL);
实际上,更仔细地观察helgrind输出,这几乎肯定会消除helgrind的抱怨,因为日志指向作为数据竞赛参与者之一的thread_s
析构函数。嗯,对s_v
的访问看起来不像是同步的。..pthread_join(s_v.th,NULL);是的,我在测试示例应用程序时也尝试过这种方法。如果在销毁s_v之前调用pthread_join,它就可以工作。但我仍然期待着其他解决方法,因为我不喜欢在销毁之前添加stop函数,如果这样的线程实例保存在一个容器中,该容器中也存在线程以外的对象。关键是,您需要保持thread\u s
对象的活动状态,至少在执行线程处于活动状态时有效。我使用了pthread\u join()
作为这个简单示例的一个简单解决方法,但是有很多方法可以做到这一点(尽管它们通常会在某个时候使用pthread\u join()
,而不是在创建线程后直接使用)。似乎有一种方法是在破坏Michael Burr建议的线程实例之前调用pthread_join,或者抑制它。@user1638062:这也是一种选择,但如果分离该线程,仍然必须抑制它。此外,您可能还应该将dtor设置为虚拟的(似乎该类计划以多态方式使用),然后您将遇到相同的问题,因为在运行派生类dtor之前无法join()。为了避免线程出现一些问题,我建议您尝试boost::thread或std::thread。。。