2011-08-19 72 views
5

我的程序崩潰隨機在一個小的情況下,我可以複製,但它在mlock.c從ntdll.dll中(這是一個VC++運行時文件)發生了,我看不到堆棧跟蹤。不過,我知道它發生在我的一個線程函數中。VC++ 2010:古怪的關鍵節錯誤

這是mlock.c代碼在程序崩潰:

void __cdecl _unlock (
     int locknum 
     ) 
{ 
     /* 
     * leave the critical section. 
     */ 
     LeaveCriticalSection(_locktable[locknum].lock); 
} 

的錯誤是「指定無效的句柄」。如果我看看locknum,它是一個大於_locktable大小的數字,所以這是有道理的。

這似乎涉及到臨界區的使用情況。我在我的線程中使用CRITICAL_SECTIONS,通過一個CCriticalSection包裝類及其相關的RAII後衛CGuard。兩個here的定義,以避免更多的混亂。

這就是會崩潰線程函數:

unsigned int __stdcall CPlayBack::timerThread(void * pParams) { 
#ifdef _DEBUG 
    DRA::CommonCpp::SetThreadName(-1, "CPlayBack::timerThread"); 
#endif 
    CPlayBack * pThis = static_cast<CPlayBack*>(pParams); 
    bool bContinue = true; 
    while(bContinue) { 
     float m_fActualFrameRate = pThis->m_fFrameRate * pThis->m_fFrameRateMultiplier; 
     if(m_fActualFrameRate != 0 && pThis->m_bIsPlaying) { 
      bContinue = (::WaitForSingleObject(pThis->m_hEndThreadEvent, static_cast<DWORD>(1000.0f/m_fActualFrameRate)) == WAIT_TIMEOUT); 
      CImage img; 
      if(pThis->m_bIsPlaying && pThis->nextFrame(img)) 
       pThis->sendImage(img); 
     } 
     else 
      bContinue = (::WaitForSingleObject(pThis->m_hEndThreadEvent, 10) == WAIT_TIMEOUT); 
    } 
    ::GetErrorLoggerInstance()->Log(LOG_TYPE_NOTE, "CPlayBack", "timerThread", "Exiting thread"); 
    return 0; 
} 

從何在CCriticalSection?每個CImage對象包含它使用經過CGuard RAII鎖CCriticalSection對象。此外,每CImage含有CSharedMemory對象,它實現的引用計數。爲此,它包含兩個CCriticalSection的以及,一個用於數據,一個用於參考計數器。這些相互作用的一個很好的例子在析構函數看得最清楚:

CImage::~CImage() { 
    CGuard guard(m_csData); 
    if(m_pSharedMemory != NULL) { 
     m_pSharedMemory->decrementUse(); 
     if(!m_pSharedMemory->isBeingUsed()){ 
      delete m_pSharedMemory; 
      m_pSharedMemory = NULL; 
     } 
    } 
    m_cProperties.ClearMin(); 
    m_cProperties.ClearMax(); 
    m_cProperties.ClearMode(); 
} 

CSharedMemory::~CSharedMemory() { 
    CGuard guardUse(m_cs); 
    if(m_pData && m_bCanDelete){ 
     delete []m_pData; 
    } 
    m_use = 0; 
    m_pData = NULL; 
} 

任何人碰到了這樣那樣的錯誤?任何建議?

編輯:我看到一些調用堆棧:調用來自〜CSharedMemory。因此,必須有一些競爭條件存在

編輯:更多CSharedMemory代碼here

+0

內存損壞? – 2011-08-19 15:21:32

+0

這兩個班本身看起來很好。你能展示一些與你如何使用它們有關的代碼嗎?你確定構造函數在使用之前被正確地調用了嗎(構造函數沒有線程爭用?)。它們是否被動態分配(出於某種原因)? – Chad

+0

你的班級與CRT代碼沒有任何關係,它使用Windows。調試線程競爭和堆腐敗從來沒有樂趣,祝你好運。 –

回答

1

我決定堅持KISS的原則和搖滾樂的所有事情都簡化了。我想我會用std::tr1::shared_ptr<BYTE>CCriticalSection替換CSharedMemoryClass,它保護它免受併發訪問。兩人現在都是CImage的成員,現在關注度更好,恕我直言。

這解決了奇怪的臨界區,但現在看來我有一個內存泄漏引起std::tr1::shared_ptr,你可能會看到我很快發佈它......它永遠不會結束!

5

「無效的句柄指定」返回代碼繪製你的臨界區對象已釋放一個非常清晰的畫面;當然假設它被正確地分配開始。

你RAII類似乎是一個可能的罪魁禍首。如果你退一步想想,你的RAII類違反Sepration Of Concerns原則,因爲它有兩個任務:

  1. 它提供分配/銷燬的語義CRITICAL_SECTION
  2. 它提供了獲取/釋放語義CRITICAL_SECTION

CS封裝器的大部分實現都是以同樣的方式違反了SoC原則,但它可能會有問題。特別是當你必須開始傳遞類的實例以獲得獲取/發佈功能時。考慮psudocode一個簡單的,人爲的例子:

void WorkerThreadProc(CCriticalSection cs) 
{ 
    cs.Enter(); 
    // MAGIC HAPPENS 
    cs.Leave(); 
} 

int main() 
{ 
    CCriticalSection my_cs; 
    std::vector<NeatStuff> stuff_used_by_multiple_threads; 

    // Create 3 threads, passing the entry point "WorkerThreadProc" 
    for(int i = 0; i < 3; ++i) 
    CreateThread(... &WorkerThreadProc, my_cs); 

    // Join the 3 threads... 
    wait(); 
} 

這裏的問題是CCriticalSection是按值傳遞,所以調用析構函數的4倍。每次調用析構函數時,都會釋放CRITICAL_SECTION。第一次工作正常,但現在它消失了。

你可以通過傳遞引用或指向臨界類的指針來解決這個問題,但是你會把所有權問題的語義水域混淆。如果「擁有」暴擊秒的線程在其他線程之前死亡,該怎麼辦?你可以使用shared_ptr,但是現在沒有人真的擁有關鍵部分,並且你已經放棄了對某個區域的一些控制,以便在另一個區域獲得一些。

這個問題的真正「修復」是單獨考慮問題。有分配&釋放一個類:

class CCriticalSection : public CRITICAL_SECTION 
{ 
public: 
    CCriticalSection(){ InitializeCriticalSection(this); } 
    ~CCriticalSection() { DestroyCriticalSection(this); } 
}; 

...,另一個把手鎖止&解鎖...

class CSLock 
{ 
public: 
    CSLock(CRITICAL_SECTION& cs) : cs_(cs) { EnterCriticalSection(&cs_); } 
    ~CSLock() { LeaveCriticalSection(&cs_); } 
private: 
    CRITICAL_SECTION& cs_; 
}; 

現在你可以圍繞原始指針或引用傳遞給一個CCriticalSection對象,可能是const,並讓工作線程在其上實例化自己的CSLocks。 CSLock由創建它的線程所擁有,這是它應該是的,但CCriticalSection的所有權顯然由一些控制線程保留;也是一件好事。

+0

我不明白這跟我以前做的有什麼不同,除了它使用繼承而不是組合,並且在相當於我的CGuard類中調用EnterCriticalSection和LeaveCriticalSection 。這樣做會得到什麼? –

+0

你改變了你的問題,現在我們不知道CCriticalSection是什麼樣子,CGuard是什麼,或者兩者如何相關。你說「每個CImage對象都包含一個通過CGuard RAII鎖定使用的CCriticalSection對象」,但除非我們看到它們是如何聲明和實現的,否則我們並不知道這意味着什麼。 –

+0

對不起,我刪除了該代碼,因爲有人告訴我我的課程沒問題,並避免混淆問題。我將其張貼在Ideone.com上,請參閱我更新的問題 –

1
  • 確保關鍵部分對象不在#pragma包裝1(或任何非默認包裝)。
  • 確保沒有其他線程(或同一線程)正在破壞CS對象。運行一些靜態分析工具來檢查是否有任何緩衝區溢出問題。
  • 如果您有運行時分析工具,請運行它來查找問題。