2010-08-26 76 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)或任何其他值。