TCriticalSection
является оболочкой для Win32 Critical Section object, где Acquire()
просто вызывает EnterCriticalSection()
и Release()
просто вызывает LeaveCriticalSection()
.
В LeaveCriticalSection()
документации говорится:
Нить использует функцию EnterCriticalSection
или TryEnterCriticalSection
приобрести в собственность критического объекта раздела. Чтобы освободить свою собственность, поток должен вызывать LeaveCriticalSection
один раз за каждый раз, когда он вступил в критический раздел.
Если поток вызывает LeaveCriticalSection
, когда он не имеет права собственности на указанный объект критической секции возникает ошибка, которая может привести к другой поток с помощью EnterCriticalSection
ждать до бесконечности.
Итак, НЕ пытайтесь разблокировать критический раздел в потоке, который в настоящее время не владеет блокировкой.
Это совершенно нормально (и предпочтительный) для вас, чтобы переместить lock
переменную внутри OneAtATimePlease()
, где она принадлежит:
void OneAtATimePlease()
{
CRITICAL_SECTION_LOCK lock(criticalSection);
...
}
Подумайте о том, что будет происходить с помощью исходного кода, если несколько потоков называют OneAtATimePlease()
на то же самое время, но поток не блокирует критическую секцию:
тему 1
CRITICAL_SECTION_LOCK lock(criticalSection);
OneAtATimePlease();
lock.Release();
Тема 2
CRITICAL_SECTION_LOCK lock(criticalSection);
OneAtATimePlease();
lock.Release();
Тема 3
// NO LOCK!!!
OneAtATimePlease();
Тема 3 может выполнить OneAtATimePlease()
в то время как Пропустите 1 или 2 уже внутри него! Это побеждает всю цель использования критического раздела. Если вы переместите блокировку внутри OneAtATimePlease()
, то нет возможности для того, чтобы несколько потоков не синхронизировались друг с другом (если только один из них не ошибочно не разблокирует критический раздел, когда он не владеет блокировкой, но ваша оболочка RAII предотвратит это) ,
Это даже работать рекурсивно и безопасно, согласно documentation:
Когда поток имеет критическую секцию, он может сделать дополнительные вызовы EnterCriticalSection
или TryEnterCriticalSection
, не блокируя его исполнение. Это предотвращает зависание потока в ожидании критической секции, которой он уже владеет. Чтобы освободить свою собственность, поток должен вызывать LeaveCriticalSection
один раз за каждый раз, когда он вступил в критический раздел.
void OneAtATimePlease()
{
CRITICAL_SECTION_LOCK lock(criticalSection);
...
if (some condition)
OneAtATimePlease();
....
}
Спасибо за четкий и подробный ответ! –