2010-08-26 74 views
2

**已解决:在我的类的构造函数中,我有一个Semaphore的构造赛车,它的线程构造是在其中我首先创建Semaphore,然后是Thread的第二个构造。为我工作的解决方案是首先在基类中创建Semaphore,这样我可以在派生类中依赖它。 **为什么这个代码在valgrind(helgrind)下失败?

我有一个相当小的pthreads C++程序,它在正常情况下工作正常。但是,在程序中使用valgrind的线程错误检查工具时,它似乎会发现竞争状态。这种竞争条件特别难以避免的是它发生在“Semaphore”类(它实际上只是封装了sem_init,sem_wait和sem_post)内,所以我不能用另一个Semaphore来解决这个问题(并且不应该)。我不认为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"); 
} 
  • 注意信号灯的构造如何创建一个名为“m_Sem”新sem_t,并在m_Sem还是等于0。这只是极,情况不太可能发生抛出异常意味着这个构造函数不应该允许m_Sem等于0.好吧...继续到Semaphore :: lock:无论从哪个线程调用此函数(以及构造函数),它都应该 - 理论上 - 对于m_Sem是不可能的,对吧?那么,当我在helgrind下运行我的程序时,Semaphore :: lock肯定会导致抛出这个异常“Semaphore :: lock error:m_Sem == 0”,我真的认为这应该是不可能的。

我在其他程序中使用这个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: [email protected]@GLIBC_2.1 (createthread.c:249) 
==7776== by 0x402950C: pthread_create_WRK (hg_intercepts.c:230) 
==7776== by 0x40295A0: [email protected]* (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可以从我的混帐回购分支检查出的代码(其中,备案,目前正在对提交262369c2d25eb17a0147)如下重现此:

 
$ git clone git://github.com/notfed/concqueue -b semaphores 
$ cd concqueue 
$ make 
$ valgrind --tool=helgrind --read-var-info=yes ./try 

回答

0

好的,我发现了这个问题。我的ActionQueue类正在创建两个对象(除别人之外):Semaphore和Thread。问题是,这个线程使用了Semaphore。我错误地认为Semaphore会在进入构造函数之前自动创建,因为它是一个成员对象。我的解决方案是从我的Semaphore构建的基类中派生出ActionQueue;这样,当我到达ActionQueue的构造函数时,我可以指望已经构建的基类的成员。

3

虽然它看起来像线程正在尝试使用线程2中的信号量,然后线程1已完成运行构造函数。在这种情况下,可以使m_Sem为NULL(0)或任何其他值。