**已解决:在我班级的构造函数中,我有一个使用Thread构造的Semaphore构造赛车,我希望首先创建Semaphore,然后创建Thread。对我有用的解决方案是首先在基类中创建信号量,这样我可以在我的派生类中依赖它。 **
我有一个相当小的pthreads C ++程序,在正常情况下工作正常。但是,当在程序上使用valgrind的线程错误检查工具时,它似乎可以发现竞争条件。是什么让这种竞争条件特别难以避免,它发生在“信号量”类(实际上只是封装sem_init,sem_wait和sem_post)中,所以我无法用另一个信号量修复它(并且不应该)。我不认为valgrind会给出误报,因为我的程序在valgrind下运行时显示出不同的行为。
这是Semaphore.cpp *:
#include "Semaphore.h" #include <stdexcept> #include <errno.h> #include <iostream> Semaphore::Semaphore(bool pshared,int initial) : m_Sem(new sem_t()) { if(m_Sem==0) throw std::runtime_error("Semaphore constructor error: m_Sem == 0"); if(sem_init(m_Sem,(pshared?1:0),initial)==-1) throw std::runtime_error("sem_init failed"); } Semaphore::~Semaphore() { sem_destroy(m_Sem); delete m_Sem; } void Semaphore::lock() { if(m_Sem==0) throw std::runtime_error("Semaphore::lock error: m_Sem == 0"); int rc; for(;;){ rc = sem_wait(m_Sem); if(rc==0) break; if(errno==EINTR) continue; throw std::runtime_error("sem_wait failed"); } } void Semaphore::unlock() { if(sem_post(m_Sem)!=0) throw std::runtime_error("sem_post failed"); }
我已经在其他程序中使用了这个Semaphore类,这些程序没有任何问题地通过helgrind,而且我真的不确定我在这里做什么特别导致问题。根据helgrind的说法,比赛发生在一个线程中的Semaphore构造函数的写入和另一个线程中的Semaphore :: lock中的读取之间。老实说,我甚至不知道这是怎么可能的:对象的方法如何与该对象的构造函数有竞争条件? C ++是否保证在可以调用对象上的方法之前调用构造函数?即使在多线程环境中,如何侵犯它?
无论如何,现在为valgrind输出。我正在使用valgind版本“Valgrind-3.6.0.SVN-Debian”。 Memcheck说一切都很顺利。这是helgrind的结果:
$ valgrind --tool=helgrind --read-var-info=yes ./try ==7776== Helgrind, a thread error detector ==7776== Copyright (C) 2007-2009, and GNU GPL'd, by OpenWorks LLP et al. ==7776== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==7776== Command: ./try ==7776== terminate called after throwing an instance of '==7776== Thread #1 is the program's root thread ==7776== ==7776== Thread #2 was created ==7776== at 0x425FA38: clone (clone.S:111) ==7776== by 0x40430EA: pthread_create@@GLIBC_2.1 (createthread.c:249) ==7776== by 0x402950C: pthread_create_WRK (hg_intercepts.c:230) ==7776== by 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== Possible data race during write of size 4 at 0x42ee04c by thread #1 ==7776== at 0x804D9C5: Semaphore::Semaphore(bool, int) (Semaphore.cpp:8) ==7776== by 0x804B333: ActionQueue::ActionQueue() (ActionQueue.h:40) ==7776== by 0x80497CA: main (try.cpp:9) ==7776== This conflicts with a previous read of size 4 by thread #2 ==7776== at 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 (clone.S:130) ==7776== std::runtime_error' what(): Semaphore::lock error: m_Sem == 0 ==7776== ==7776== For counts of detected and suppressed errors, rerun with: -v ==7776== Use --history-level=approx or =none to gain increased speed, at ==7776== the cost of reduced accuracy of conflicting-access information ==7776== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 5)
任何拥有git和valgrind的人都可以通过检查我的git repo分支(对于记录,目前在提交262369c2d25eb17a0147)中的代码来重现这一点,如下所示:
$ git clone git://github.com/notfed/concqueue -b semaphores $ cd concqueue $ make $ valgrind --tool=helgrind --read-var-info=yes ./try
答案 0 :(得分:3)
虽然看起来线程在线程1完成运行构造函数之前尝试在线程2中使用信号量。在这种情况下,可以使m_Sem为NULL(0)或任何其他值。
答案 1 :(得分:0)
好的,我发现了问题。我的ActionQueue类在构造时创建了两个对象(除了其他对象):一个信号量和一个线程。问题是,这个线程正在使用那个信号量。我错误地认为信号量会在进入构造函数之前自动创建,因为它是一个成员对象。我的解决方案是从构建我的信号量的基类派生ActionQueue;这样,当我到达ActionQueue的构造函数时,我可以依赖已经构建的基类成员。