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;

unsigned int __stdcall LockThreadProc(void * lpParam)
{
lock.Lock(1, reinterpret_cast(lpParam)); //lock resource

//do something here

lock.Unlock( reinterpret_cast(lpParam)); //unlock resource

//contuinue working

return 0;
}

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 - захват ресурса есть инициализация. Суть ее в следующем: создается класс-обертка такой, что в конструкторе класса вызывается соответствующая функция блокировки ресурса, а в деструкторе блокировка снимается. Это удобно по двум причинам:

  1. Нет необходимости делать явный вызов Unlock (а это часто, как показывает практика, забывают сделать).
  2. В случае возбуждения исключения между вызовами Lock и Unlock ресурс может оказаться занят “навсегда”. А при использования RAII, деструктор класса-обертки, а следовательно и Unlock, будет вызван и в случае исключительной ситуации.

VLockPtr - RAII класс-обертка над VLock.

VReadLockPtr - RAII класс-обертка над VRWLock::LockRead.

VWriteLockPtr - RAII класс-обертка над VRWLock::LockWrite.

unsigned int __stdcall LockPtrThreadProc(void * lpParam)
{
{
VLockPtr lockptr(&lock, 1, reinterpret_cast(lpParam)); //lock resource

//do something here

} //unlock will be done here

//contuinue working

return 0;
}

===

Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru