クリティカル セクションの使用に問題があります。私のアプリには多数のスレッド (たとえば 60) があり、それらすべてがグローバル リソースにアクセスする必要があります。したがって、そのリソースをクリティカル セクションで保護します。これは操作中は完全に機能しますが、アプリケーションがシャットダウンすると、スレッドが終了し、クリティカル セクションが破棄されます。
問題は、これらのスレッドの一部が終了時にクリティカル セクションで待機している場合に発生し、その結果、スレッド自体の終了がブロックされます。
「Initialized」フラグを持つ Windows CriticalSection 呼び出しのラッパーを作成しました。このフラグは、クリティカルが作成されたときに true に設定され、クリティカルを離れようとしているときに false に設定されます (どちらの場合も、内部にあるときに設定されます)。クリティカル)。このフラグは、'enter crit' ラッパー関数がクリティカルに入ろうとする前にチェックされ、フラグが false の場合はリクエストをバイパスします。フラグは、スレッドが正常にクリティカルに入った瞬間にもチェックされ、false の場合はすぐにクリティカルから離れます。
クリティカルを削除する前に私がすべきことは、フラグを false に設定してから、待機中のスレッドが次のようになるまで待機することです。Initialized フラグが false であることを確認してください。次に、クリティカルを残します(これは、各スレッドの操作が非常に迅速になるはずです)。
CRITICAL_SECTION 構造体内の LockCount をチェックすることで、クリティカルへのアクセスを待機しているスレッドの数をチェックし、それが 0 になるまで待ちます (XP ではLockCount - (RecursionCount-1)
; 2003 サーバー以降では、ロック カウントは です((-1) - (LockCount)) >> 2
)。クリティカルセクション。
これで十分なはずですが、クリティカルに入るのを待っているスレッドが 1 つ (常に 1 つのスレッドのみで、それ以上になることはありません) ある場合、LockCount が 0 に達することがわかりました。つまり、その時点でクリティカルを削除すると、他のスレッドが引き続きウェイクします。 CRITICAL_SECTION オブジェクトがその時点までに破棄されているため、クラッシュが発生します。
アクセスを待機しているスレッドの内部ロック数を保持している場合、正しい数が得られます。ただし、このカウントをクリティカルの外でインクリメントする必要があるため、これは理想的ではありません。つまり、値は保護されていないため、一度に完全に信頼することはできません。
CRITICAL_SECTION 構造体の LockCount が 1 になる理由を知っている人はいますか? 独自のロック カウントを使用する場合、最後のスレッドが終了した後(およびクリティカルを破棄する前) にCRITICAL_SECTION のロック カウントを確認すると、まだ 0 です...
または、クリティカル セクション以外に、アプリ内のグローバル リソースを多くのスレッドで保護するためのより良い方法はありますか?
これは私のラッパー構造です:
typedef struct MY_CRIT {
BOOL Initialised;
CRITICAL_SECTION Crit;
int MyLockCount;
}
これが私の Crit init 関数です。
BOOL InitCrit( MY_CRIT *pCrit )
{
if (pCrit)
{
InitializeCriticalSection( &pCrit->Crit );
pCrit->Initialised = TRUE;
pCrit->MyLockCount = 0;
return TRUE;
}
// else invalid pointer
else
return FALSE;
}
これは私のEnter Critラッパー関数です:
BOOL EnterCrit( MY_CRIT *pCrit )
{
// if pointer valid, and the crit is initialised
if (pCrit && pCrit->Initialised)
{
pCrit->MyLockCount++;
EnterCriticalSection( &pCrit->Crit );
pCrit->MyLockCount--;
// if still initialised
if (pCrit->Initialised)
{
return TRUE;
}
// else someone's trying to close this crit - jump out now!
else
{
LeaveCriticalSection( &pCrit->Crit );
return FALSE;
}
}
else // crit pointer is null
return FALSE;
}
そして、これが私の FreeCrit ラッパー関数です。
void FreeCrit( MY_CRIT *pCrit )
{
LONG WaitingCount = 0;
if (pCrit && (pCrit->Initialised))
{
// set Initialised to FALSE to stop any more threads trying to get in from now on:
EnterCriticalSection( &pCrit->Crit );
pCrit->Initialised = FALSE;
LeaveCriticalSection( &pCrit->Crit );
// loop until all waiting threads have gained access and finished:
do {
EnterCriticalSection( &pCrit->Crit );
// check if any threads are still waiting to enter:
// Windows XP and below:
if (IsWindowsXPOrBelow())
{
if ((pCrit->Crit.LockCount > 0) && ((pCrit->Crit.RecursionCount - 1) >= 0))
WaitingCount = pCrit->Crit.LockCount - (pCrit->Crit.RecursionCount - 1);
else
WaitingCount = 0;
}
// Windows 2003 Server and above:
else
{
WaitingCount = ((-1) - (pCrit->Crit.LockCount)) >> 2;
}
// hack: if our own lock count is higher, use that:
WaitingCount = max( WaitingCount, pCrit->MyLockCount );
// if some threads are still waiting, leave the crit and sleep a bit, to give them a chance to enter & exit:
if (WaitingCount > 0)
{
LeaveCriticalSection( &pCrit->Crit );
// don't hog the processor:
Sleep( 1 );
}
// when no other threads are waiting to enter, we can safely delete the crit (and leave the loop):
else
{
DeleteCriticalSection( &pCrit->Crit );
}
} while (WaitingCount > 0);
}
}