LockLib
LockLib это набор классов для организации доступа к разделяемым ресурсам в программе на C++ под Windows.
Исходники доступны на GitHub: https://github.com/coolsoftware/LockLib.
class VLock
Класс VLock используется как альтернатива CRITICAL_SECTION (на самом деле это “обертка” над CRITICAL_SECTION).
void Lock(int lPosition, volatile LONG * lpThreadLock = NULL)
Блокировка ресурса для монопольного использования. Если ресурс уже кем-то заблокирован, то происходит ожидание когда ресурс снова станет свободен и его удастся заблокировать.
Параметр lPosition служит для идентификации места вызова метода Lock и может использоваться при отладке.
Необязательный параметр lpThreadLock служит для подсчета вызовов метода Lock в текущем потоке. Подробности смотрите ниже в разделе посвященном lpThreadLock.
void Unlock(volatile LONG * lpThreadLock = NULL)
Снятие блокировки.
static void OutputDebugLocks()
При отладке и оптимизации приложений иногда нужно видеть список всех существующих блокировок и статистику по ним: сколько в данный момент активных блокировок, в каком месте они заблокированы. Посмотреть такую статистику можно вызвав OutputDebugLocks. Эта статистика доступна в Debug-версии приложении, когда объявлен _DEBUG, или когда объявлен макрос DEBUG_LOCK.
volatile LONG * lpThreadLock
Проблема с блокировками в много-поточном приложении связана с тем, что поток, который установил блокировку, может быть остановлен с помощью TerminateThread. В этом случае Unlock не будет вызван никогда и блокировка ресурса “повиснет”, а другие потоки, которые попытаются заблокировать этот ресурс, также “повиснут”.
Чтобы разрулить эту ситуацию, необходимо вести подсчет блокировок для каждого потока и вызывать Unlock после TerminateThread.
VLock lock; |
class VRWLock
Класс VRWLock позволяет реализовать стратегию блокировки ресурса “один писатель - много читателей”.
VRWLock(LONG lMaxReaders = 65535, DWORD dwSpinCount = 1000, DWORD dwTimeout = 5)
Конструктор класса VRWLock имеет следующие параметры:
lMaxReaders - максимальное количество читателей, которые могут одновременно читать ресурс (не должно быть = 0!). Значение по-умолчанию 65535.
dwSpinCount - количество неудачных попыток блокировки ресурса (занятого), после которых происходит переключение контекста с небольшой задержкой, определяемой параметром dwTimeout (в миллисекундах).
void LockRead(int lPosition, volatile LONG * lpThreadLock = NULL)
Блокировка ресурса читателем. Если ресурс занят писателем или превышено максимальное количество читателей, то происходит ожидание освобождения ресурса, когда удастся его заблокировать. Смотрите описание параметров в описании метода Lock класса VLock.
void LockWrite(int lPosition, volatile LONG * lpThreadLock = NULL)
Блокировка ресурса писателем. Если ресурс занят писателем или одним или несколькими читателями, то происходит ожидание освобождения ресурса, когда удастся его заблокировать. Смотрите описание параметров в описании метода Lock класса VLock.
void ReLockWrite(int lPosition, volatile LONG * lpThreadLock = NULL)
Изменение статуса блокировки с читателя на писателя. Если ресурс не занят ни читателем ни писателем, то действие функции аналогично LockWrite (блокировка писателем). Если ресурс занят одним читателем, то происходит переключение его статуса с читателя на писателя. Если ресурс занят писателем или более чем одним читателем, то происходит ожидание момента, когда ресурс будет не занят писателями и занят не более чем одним читателем. Смотрите описание параметров в описании метода Lock класса VLock.
void Unlock(volatile LONG * lpThreadLock = NULL)
Снятие блокировки.
static void OutputDebugLocks()
Вывод статистики блокировок (см. выше описание одноименного метода для класса VLock).
class VLockPtr, class VReadLockPtr, class VWriteLockPtr
Есть такая идиома: RAII - захват ресурса есть инициализация. Суть ее в следующем: создается класс-обертка такой, что в конструкторе класса вызывается соответствующая функция блокировки ресурса, а в деструкторе блокировка снимается. Это удобно по двум причинам:
- Нет необходимости делать явный вызов Unlock (а это часто, как показывает практика, забывают сделать).
- В случае возбуждения исключения между вызовами Lock и Unlock ресурс может оказаться занят “навсегда”. А при использования RAII, деструктор класса-обертки, а следовательно и Unlock, будет вызван и в случае исключительной ситуации.
VLockPtr - RAII класс-обертка над VLock.
VReadLockPtr - RAII класс-обертка над VRWLock::LockRead.
VWriteLockPtr - RAII класс-обертка над VRWLock::LockWrite.
unsigned int __stdcall LockPtrThreadProc(void * lpParam) |
===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru