Код возврата «указан неверный дескриптор» рисует довольно четкую картину того, что ваш объект критической секции был освобожден;при условии, конечно, что он был выделен правильно с самого начала.
Ваш класс RAII кажется вероятным виновником.Если вы сделаете шаг назад и подумаете об этом, ваш класс RAII нарушает принцип Sepration of Concerns , потому что у него есть два задания:
- Он обеспечивает семантику выделения / уничтожения дляCRITICAL_SECTION
- Предоставляет семантику получения / выпуска для CRITICAL_SECTION
Большинство реализаций оболочки CS, которые я видел, аналогичным образом нарушают принцип SoC, но это может быть проблематично.Особенно, когда вам нужно начать передавать экземпляры класса, чтобы получить функциональность получения / выпуска.Рассмотрим простой надуманный пример в псевдокоде:
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 явно сохраняется некоторым управляющим потоком;тоже хорошая вещь.