Windows用ポータブルRWLock

RWLockは、同時読み取りと排他的書き込みを可能にする同期プリミティブです。 つまり 読み取りは書き込みをブロックしますが、他のスレッドの読み取りはブロックせず、書き込みはすべてをブロックします。



したがって、この非常に便利なプリミティブは、posixスレッドおよびVista以降のWindowsで使用できます。 Windows XP / 2003の場合、2つの重要なセクションとイベントから作成する必要があります(実際には、UPDの方が良いオプションです。記事の最後にあるUPDを参照してください)。



それがどのように見えるかを見てみましょう(コードはStackOverflowによって親切に提供され、CからC ++にわずかに翻訳されました):



class RWLockXP // Implementation for Windows XP { public: RWLockXP() : countsLock(), writerLock(), noReaders(), readerCount(0), waitingWriter(FALSE) { InitializeCriticalSection(&writerLock); InitializeCriticalSection(&countsLock); /* * Could use a semaphore as well. There can only be one waiter ever, * so I'm showing an auto-reset event here. */ noReaders = CreateEvent (NULL, FALSE, FALSE, NULL); } ~RWLockXP() { DeleteCriticalSection(&writerLock); DeleteCriticalSection(&countsLock); CloseHandle(noReaders); } 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. */ EnterCriticalSection(&writerLock); EnterCriticalSection(&countsLock); ++readerCount; LeaveCriticalSection(&countsLock); LeaveCriticalSection(&writerLock); } void readUnLock() { EnterCriticalSection(&countsLock); assert (readerCount > 0); if (--readerCount == 0) { if (waitingWriter) { /* * Clear waitingWriter here to avoid taking countsLock * again in wrlock. */ waitingWriter = FALSE; SetEvent(noReaders); } } LeaveCriticalSection(&countsLock); } void writeLock() { EnterCriticalSection(&writerLock); /* * readerCount cannot become non-zero within the writerLock CS, * but it can become zero... */ if (readerCount > 0) { EnterCriticalSection(&countsLock); /* ... so test it again. */ if (readerCount > 0) { waitingWriter = TRUE; LeaveCriticalSection(&countsLock); WaitForSingleObject(noReaders, INFINITE); } else { /* How lucky, no need to wait. */ LeaveCriticalSection(&countsLock); } } /* writerLock remains locked. */ } void writeUnLock() { LeaveCriticalSection(&writerLock); } private: CRITICAL_SECTION countsLock; CRITICAL_SECTION writerLock; HANDLE noReaders; int readerCount; BOOL waitingWriter; };
      
      





そして、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で最初のオプションを使用し、新しいシステムで2番目のオプションを使用したい)、「ああ、しかし、 InitializeSRWLock関数、kernel32で何かが見つかりませんでした。その後、アプリケーションが親切に釘付けになります。



解決策は、LoadLibrary、関数ポインターを使用して、スリムRWLock関数を動的に読み込むことです。



 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; };
      
      





そして、オートロッカーの最後に:



 class ScopedRWLock { public: ScopedRWLock(RWLock *lc_, bool write_ = false) : lc(*lc_), write(write_) { if (write) { lc.writeLock(); } else { lc.readLock(); } } ~ScopedRWLock() { if (write) { lc.writeUnLock(); } else { lc.readUnLock(); } } private: RWLock &lc; bool write; // Non copyable! static void *operator new(size_t); static void operator delete(void *); ScopedRWLock(const ScopedRWLock&); void operator=(const ScopedRWLock&); };
      
      





pthreadを使用した実装は、呼び出された関数の他の名前を除いて、SRWLockの最初のバージョンと同じです。



UPD:

ここでは、ドキュメント化されていないすばらしい機能があり(Windows 2000に既に存在し、まだ存在している(2017年1月31日現在、Windows 10ではどこにも行っていない))、イベントやミューテックスの松葉杖よりも優れいること示唆しました。

私はそれらを使用するオプションを提供します(使用することをお勧めします)



 typedef struct _RTL_RWLOCK { RTL_CRITICAL_SECTION rtlCS; HANDLE hSharedReleaseSemaphore; UINT uSharedWaiters; HANDLE hExclusiveReleaseSemaphore; UINT uExclusiveWaiters; INT iNumberActive; HANDLE hOwningThreadId; DWORD dwTimeoutBoost; PVOID pDebugInfo; } RTL_RWLOCK, *LPRTL_RWLOCK; typedef void(__stdcall *RtlManagePtr)(LPRTL_RWLOCK); typedef BYTE(__stdcall *RtlOperatePtr)(LPRTL_RWLOCK, BYTE); class RWLockXP // Implementation for Windows XP { public: RWLockXP() : hGetProcIDDLL(NULL), RtlDeleteResource_func(NULL), RtlReleaseResource_func(NULL), RtlAcquireResourceExclusive_func(NULL), RtlAcquireResourceShared_func(NULL), rtlRWLock() { wchar_t path[MAX_PATH] = { 0 }; GetSystemDirectory(path, sizeof(path)); std::wstring dllPath = std::wstring(path) + L"\\ntdll.dll"; HINSTANCE hGetProcIDDLL = LoadLibrary(dllPath.c_str()); if (hGetProcIDDLL) { RtlDeleteResource_func = (RtlManagePtr)GetProcAddress(hGetProcIDDLL, "RtlDeleteResource"); if (!RtlDeleteResource_func) { return; } RtlReleaseResource_func = (RtlManagePtr)GetProcAddress(hGetProcIDDLL, "RtlReleaseResource"); if (!RtlReleaseResource_func) { return; } RtlAcquireResourceExclusive_func = (RtlOperatePtr)GetProcAddress(hGetProcIDDLL, "RtlAcquireResourceExclusive"); if (!RtlAcquireResourceExclusive_func) { return; } RtlAcquireResourceShared_func = (RtlOperatePtr)GetProcAddress(hGetProcIDDLL, "RtlAcquireResourceShared"); if (!RtlAcquireResourceShared_func) { return; } RtlManagePtr RtlInitializeResource_func = (RtlManagePtr)GetProcAddress(hGetProcIDDLL, "RtlInitializeResource"); if (RtlInitializeResource_func) { RtlInitializeResource_func(&rtlRWLock); } } } ~RWLockXP() { if (RtlDeleteResource_func) { RtlDeleteResource_func(&rtlRWLock); } if (hGetProcIDDLL) { FreeLibrary(hGetProcIDDLL); } } void readLock() { if (RtlAcquireResourceShared_func) { RtlAcquireResourceShared_func(&rtlRWLock, TRUE); } } void readUnLock() { if (RtlReleaseResource_func) { RtlReleaseResource_func(&rtlRWLock); } } void writeLock() { if (RtlAcquireResourceExclusive_func) { RtlAcquireResourceExclusive_func(&rtlRWLock, TRUE); } } void writeUnLock() { if (RtlReleaseResource_func) { RtlReleaseResource_func(&rtlRWLock); } } private: HINSTANCE hGetProcIDDLL; RtlManagePtr RtlDeleteResource_func; RtlManagePtr RtlReleaseResource_func; RtlOperatePtr RtlAcquireResourceExclusive_func; RtlOperatePtr RtlAcquireResourceShared_func; RTL_RWLOCK rtlRWLock; };
      
      






All Articles