2012-08-31 16 views
5

被稱爲當我開始學習的valgrind(helgrind)工具,我遇到了,我沒能解決這樣的問題teedy。爲什麼的valgrind(helgrind)的情況下,虛函數產生「可能的數據爭」在我的線程結構

簡單,用戶定義的線程級與這將由線程的入口程序中調用虛函數創建。如果是這種情況,helgrind會報告Possible-data-race。但是在簡單地省略虛擬關鍵字之後,將不會報告這樣的錯誤。 這是怎麼發生的?我的代碼有問題嗎?還是有解決方法?

下面是一個簡單的多線程應用程序演示這樣的問題,包括CPP,Makefile文件和消息helgrind報告。

/* 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: [email protected]@GLIBC_2.1 (createthread.c:256) 
==7477== by 0x4026E2D: pthread_create_WRK (hg_intercepts.c:257) 
==7477== by 0x4026F8B: [email protected]* (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) 
+0

那麼,對's_v'的訪問看起來不像它的同步.. –

回答

2

在一種情況是寫入vptr,另一種情況是讀取vptr。兩者都沒有被鎖定。 Helgrind無法知道程序中是否有其他方法使這種情況不可能在兩個線程中同時發生,因此它會標記它。如果可以保證在另一個線程中有人試圖調用其上的某個函數時該對象不會被銷燬,那麼您可以爲此生成一個抑制。

+0

好像是在破壞Michael Burr建議的這樣的線程實例之前調用pthread_join,或者抑制它。 – user1638062

+0

@ user1638062:這也是一種替代方案,但如果您將該線程分開,則仍必須將其禁用。同時你應該讓你的虛擬析構函數(好像按照計劃,班多態使用),然後你會遇到同樣的問題,因爲()的派生類的析構函數運行之前,你不能加入。爲了避免線程的一些問題,我建議你嘗試boost :: thread或std :: thread ... – PlasmaHH

2

我不知道這是helgrind的投訴的原因,但你必須在你的程序的一個嚴重問題。您創建一個線程,將指針傳遞到本地thread_s實例(s_vmain())。

然而,main()將很快返回,沒有任何形式與線程同步的 - 沒有什麼可以保證s_v還活着的時候線程函數routine()獲取指針的保持,並用它來調用pV->routine()

若見pthread_create()調用後添加以下防止helgrind從抱怨:

pthread_join(s_v.th, NULL); 

事實上,在helgrind輸出更緊密地尋找,這幾乎肯定會刪除helgrind的投訴,因爲日誌被指向作爲數據競賽中的一名參與者的析構函數。

+0

pthread_join(s_v.th,NULL); – user1638062

+0

pthread_join(s_v.th,NULL); 是的,當我測試該示例應用程序時,我也嘗試了這種方式。如果p_read_join在s_v被破壞之前被調用,它就會起作用。 但我仍然期待着其他的解決辦法,因爲我不喜歡自毀前添加停止功能,如果這樣的線程情況下都保持在對象以外的其他線程也存在一個容器。 – user1638062

+0

關鍵是你需要保持thread_s對象有效,至少和執行線程一樣長。我使用'pthread_join()'作爲這個簡單示例的簡單解決方法,但是有很多方法可以做到這一點(儘管它們通常以某個時刻使用的'pthread_join()'結束,而不是在創建之後線程)。 –

相關問題