RWLock - это такой примитив синхронизации, позволяющий одновременное чтение и эксклюзивную запись. Т.е. чтение блокирует запись, но не блокирует чтение других тредов,а запись блокирует все.
Так вот, этот весьма полезный примитив имеется в posix threads и в Windows от Vista и далее. Для Windows XP/2003 приходится колхозить сей, весьма полезный, примитив из говна и палок двух критических секций и события.
Покажем как это выглядит (код любезно предоставлен StackOverflow и слегка доработан напильником под использование FastMutex из TinyMT):
class RWLockXP // Implementation for Windows XP
{
public:
RWLockXP()
: readerCountLock(),
writerLock(),
noReaders(),
readerCount(0)
{
readerCountLock.create();
writerLock.create();
/**
* We use a manual-reset event as poor man condition variable that
* can only do broadcast. Actually, only one thread will be waiting
* on this at any time, because the wait is done while holding the
* writerLock.
*/
noReaders.create(true, true);
}
~RWLockXP()
{
writerLock.destroy();
readerCountLock.destroy();
noReaders.destroy();
}
void readLock()
{
/**
* We need to lock the writerLock too, otherwise a writer could
* do the whole of rwlock_wrlock after the readerCount changed
* from 0 to 1, but before the event was reset.
*/
ScopedLock<FastMutex> wlock(&writerLock);
ScopedLock<FastMutex> rlock(&readerCountLock);
if (++readerCount == 1)
{
noReaders.reset();
}
}
void readUnLock()
{
ScopedLock<FastMutex> lock(&readerCountLock);
assert (readerCount > 0);
if (--readerCount == 0)
{
noReaders.set();
}
}
void writeLock()
{
writerLock.enter();
if (readerCount > 0)
{
noReaders.wait();
}
/* writerLock remains locked. */
}
void writeUnLock()
{
writerLock.leave();
}
private:
FastMutex readerCountLock;
FastMutex writerLock;
Event noReaders;
size_t readerCount;
};
А вот как эта же красота могла бы выглядеть при использовании только Vista+ систем:
class RWLockSRW // For Windows Vista+ based on Slim RWLock
{
public:
RWLockSRW()
: srwLock()
{
InitializeSRWLock(&srwLock);
}
~RWLockSRW()
{
}
void readLock()
{
AcquireSRWLockShared(&srwLock);
}
void readUnLock()
{
ReleaseSRWLockShared(&srwLock);
}
void writeLock()
{
AcquireSRWLockExclusive(&srwLock);
}
void writeUnLock()
{
ReleaseSRWLockExclusive(&srwLock);
}
private:
RTL_SRWLOCK srwLock;
};
Мало того, что выглядит до безобразия просто, еще и работает на порядок быстрее. Но, есть одно но, как всегда :) При попытке запустить приложение содержащее этот код (конечно мы умные ребята, сделали определение версии и для XP хотим использовать первый вариант, а для новых систем - второй), получим сообщение типа: "ой, а вот функции InitializeSRWLock что-то не нашлось в kernel32" после чего наше приложение любезно будет прибито.
Выход - грузить функции Slim RWLock динамически при помощи LoadLibrary, указателей на функции и этого всего:
typedef void(__stdcall *SRWLock_fptr)(PSRWLOCK);
class RWLockSRW // For Windows Vista+ based on Slim RWLock
{
public:
RWLockSRW()
: hGetProcIDDLL(NULL),
AcquireSRWLockShared_func(NULL),
ReleaseSRWLockShared_func(NULL),
AcquireSRWLockExclusive_func(NULL),
ReleaseSRWLockExclusive_func(NULL),
srwLock()
{
wchar_t path[MAX_PATH] = { 0 };
GetSystemDirectory(path, sizeof(path));
std::wstring dllPath = std::wstring(path) + L"\\kernel32.dll";
HINSTANCE hGetProcIDDLL = LoadLibrary(dllPath.c_str());
if (!hGetProcIDDLL)
{
throw std::exception("SRWLock Error loading kernel32.dll");
}
AcquireSRWLockShared_func = (SRWLock_fptr)GetProcAddress(hGetProcIDDLL, "AcquireSRWLockShared");
if (!AcquireSRWLockShared_func)
{
throw std::exception("SRWLock Error loading AcquireSRWLockShared");
}
ReleaseSRWLockShared_func = (SRWLock_fptr)GetProcAddress(hGetProcIDDLL, "ReleaseSRWLockShared");
if (!ReleaseSRWLockShared_func)
{
throw std::exception("SRWLock Error loading ReleaseSRWLockShared");
}
AcquireSRWLockExclusive_func = (SRWLock_fptr)GetProcAddress(hGetProcIDDLL, "AcquireSRWLockExclusive");
if (!AcquireSRWLockExclusive_func)
{
throw std::exception("SRWLock Error loading AcquireSRWLockExclusive");
}
ReleaseSRWLockExclusive_func = (SRWLock_fptr)GetProcAddress(hGetProcIDDLL, "ReleaseSRWLockExclusive");
if (!ReleaseSRWLockExclusive_func)
{
throw std::exception("SRWLock Error loading ReleaseSRWLockExclusive");
}
SRWLock_fptr InitializeSRWLock_func = (SRWLock_fptr)GetProcAddress(hGetProcIDDLL, "InitializeSRWLock");
if (!InitializeSRWLock_func)
{
throw std::exception("SRWLock Error loading InitializeSRWLock");
}
InitializeSRWLock_func(&srwLock);
}
~RWLockSRW()
{
if (hGetProcIDDLL)
{
FreeLibrary(hGetProcIDDLL);
}
}
void readLock()
{
if (AcquireSRWLockShared_func)
{
AcquireSRWLockShared_func(&srwLock);
}
}
void readUnLock()
{
if (ReleaseSRWLockShared_func)
{
ReleaseSRWLockShared_func(&srwLock);
}
}
void writeLock()
{
if (AcquireSRWLockExclusive_func)
{
AcquireSRWLockExclusive_func(&srwLock);
}
}
void writeUnLock()
{
if (ReleaseSRWLockExclusive_func)
{
ReleaseSRWLockExclusive_func(&srwLock);
}
}
private:
HINSTANCE hGetProcIDDLL;
SRWLock_fptr AcquireSRWLockShared_func;
SRWLock_fptr ReleaseSRWLockShared_func;
SRWLock_fptr AcquireSRWLockExclusive_func;
SRWLock_fptr ReleaseSRWLockExclusive_func;
RTL_SRWLOCK srwLock;
};
Выглядеть стало кучеряво, зато стало портабельно.
Финальный аккорд этой пьесы - сделать враппер автоматически выбирающий нужный вариант в зависимости от версии Windows:
class RWLock // Wrapper
{
public:
RWLock()
: rwLockXP(NULL), rwLockSRW(NULL), isVistaPlus(IsWindowsVistaOrGreater())
{
if (isVistaPlus)
{
rwLockSRW = new RWLockSRW();
}
else
{
rwLockXP = new RWLockXP();
}
}
~RWLock()
{
if (isVistaPlus)
{
delete rwLockSRW;
}
else
{
delete rwLockXP;
}
}
void readLock()
{
if (isVistaPlus)
{
rwLockSRW->readLock();
}
else
{
rwLockXP->readLock();
}
}
void readUnLock()
{
if (isVistaPlus)
{
rwLockSRW->readUnLock();
}
else
{
rwLockXP->readUnLock();
}
}
void writeLock()
{
if (isVistaPlus)
{
rwLockSRW->writeLock();
}
else
{
rwLockXP->writeLock();
}
}
void writeUnLock()
{
if (isVistaPlus)
{
rwLockSRW->writeUnLock();
}
else
{
rwLockXP->writeUnLock();
}
}
private:
RWLockXP *rwLockXP;
RWLockSRW *rwLockSRW;
bool isVistaPlus;
};
Вот как-то так. Версию для pthread и автолокер предлагаю написать самостоятельно или использовать из TinyMT
Эта же статья на Хабре: https://habrahabr.ru/post/317958/