2011-08-19 21 views
5

私のプログラムは、再現できる小さなシナリオでランダムにクラッシュしますが、ntdll.dllのmlock.c(これはVC++ランタイムファイルです)で発生します。スタックトレース。私はそれが私のスレッド関数のいずれかで起こることを知っています。VC++ 2010:奇妙なクリティカルセクションエラー

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

エラー「指定された無効なハンドル」です:

これは、プログラムがクラッシュしmlock.cコードです。 locknumを見ると、_locktableのサイズよりも大きい数字なので、これは意味があります。

これはクリティカルセクションの使用に関連しているようです。私は、CCriticalSectionラッパークラスとそれに関連するRAIIガード、CGuardを介して、自分のスレッドでCRITICAL_SECTIONSを使用します。より多くの乱雑さを避けるために、両方ともの定義は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オブジェクトが含まれています。そのために、2つのCCriticalSectionも含まれています.1つはデータ用、もう1つは参照カウンタ用です。これらの相互作用の良い例は、デストラクタで見られる最高です。この種のエラーにぶつかっ

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

メモリ破損? –

+0

2つのクラス自体は上手く見えます。あなたはそれらをどのように使用しているかに関するいくつかのコードを表示できますか?コンストラクタが正しく使用される前に適切に呼び出されていることを確認してください(コンストラクタのスレッド競合はありませんか?)。動的に(何らかの理由で)割り当てられていますか? – Chad

+0

あなたのクラスはCRTコードとは何の関係もなく、Windowsを使用しています。スレッドレースとヒープの破損をデバッグすることは決して楽しいことではありません。 –

答えて

1

私はKISSの原則に従うことに決めました。ロックンロールすべてのナイトは事を単純化します。 CSharedMemoryClassstd::tr1::shared_ptr<BYTE>と置き換え、同時アクセスから保護するCCriticalSectionと置き換えました。どちらも現在CImageのメンバーであり、懸念事項は今すぐ分かっています、IMHO。

これは奇妙なクリティカルセクションを解決しましたが、現在はstd::tr1::shared_ptrによってメモリリークが発生しているようですが、すぐに私が投稿することがあります。

5

「無効なハンドル指定された」リターン・コードは、あなたのクリティカルセクションオブジェクトが割り当て解除されたかなり明確映像を塗ります。もちろん、それが最初に適切に割り当てられたと仮定します。

あなたのRAIIクラスはおそらく犯人のようです。あなたが戻って一歩を踏み出すと、考えてみれば、あなたのRAIIクラスがSepration Of Concerns原則に違反し、それは2つのジョブがあるため:

  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の割り当てが解除されます。最初はうまく動作しますが、今は消えてしまいました。

参照またはポインタをクリティカルセクションクラスに渡すことでこの問題を回避することができますが、次に所有権の問題がある意味論的ウォーターを泥まかせます。もしcritを「所有している」スレッドが他のスレッドの前に死んだ場合、どうなるでしょうか? shared_ptrを使うこともできますが、今は誰もクリティカルセクションを本当に「所有」していないので、別のエリアで少しでも利益を得るためにエリアを少しコントロールしてしまいました。

この問題に対する真の "修正"は、懸念を分離することです。 、

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

は今、あなたは、単一のCCriticalSectionオブジェクトに生のポインタまたは参照の周りに渡すことができます...

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

...と&ロック解除をロックする処理するために別:割り当て&解除のための1つのクラスを持っていますおそらくconstであり、ワーカースレッドが独自のCSLockをインスタンス化するようにします。 CSLockは、それを作成したスレッドによって所有されていますが、CCriticalSectionの所有権は、制御スレッドによって明確に保持されています。また良いことです。

+0

これは、コンストラクションの代わりに継承を使用し、EnterCriticalSectionとLeaveCriticalSectionを私のCGuardクラスに相当するものとして呼び出す点を除いて、 。そんなことから何を得るのですか? –

+0

あなたは、CCriticalSectionの外観、CGuardの種類、またはどちらが関連しているかわからないという点に質問を変更しました。 「すべてのCImageオブジェクトには、CGuard RAIIロックを使用するCCriticalSectionオブジェクトが含まれています」というようなことがありますが、宣言され実装されている方法がわからない限り、その意味がわかりません。 –

+0

申し訳ありませんが、私のクラスは大丈夫だと誰かが私に言ったので、そのコードを削除し、質問が乱雑になるのを避けるために。私はIdeone.comに投稿しました。更新された質問 –

1
  • クリティカルセクションオブジェクトが#pragmaパッキング1(またはデフォルト以外のパッキング)でないことを確認してください。
  • 他のスレッド(または同じスレッド)がCSオブジェクトを破損していないことを確認します。一部の静的解析ツールを実行して、バッファーオーバーランの問題がないか調べます。
  • 実行時分析ツールを使用している場合は、それを実行して問題を見つけます。