Задача

Є певний ресурс (черга, файл, таблиця, тощо) з якого одночасно може читати довільна кількість нитей-читачів, але писати одночасно може лише одна нить-письменник.

Треба реалізувати сихнхронізацію цього ресурсу.

Рішення

Найпростішим рішенням було б синхронізувати кожну операцію з ресурсом, але це буде дуже неефективно бо кілька читачів можуть читати одночасно не змінюючи ресурс.

Можна зробити наступне:

  • коли письменнику треба виконати якусь операцію він блокує ресурс і чекає доки всі читатчі завершать свою роботу (подія “нема жодного читача”)
  • Коли письменник завершує свою роботу він просто розблоковує ресурс.
  • Читатч перед тим як почати читати робить наступне: блокує ресурс, збільшує лічильник читачів і якщо той був 0 до того вимикає подфю “нема жодного читача” після чого звільняє ресурс
  • Читач коли завершує роботу з ресурсом зменшує лічильник читачів і якщо він став нулевим то встановлює подію “нема жодного читача”.

Для блокування ресурсу скористаємося критичною секцією. Для події “нема жодного читача” – подією, і для захисту лічильника – критичною секцією.

Код взято звідси – http://www.glennslayden.com/code/win32/reader-writer-lock

#include "windows.h" class MultiReaderSingleWriter { private: CRITICAL_SECTION m_csWrite; CRITICAL_SECTION m_csReaderCount; long m_cReaders; HANDLE m_hevReadersCleared; public: MultiReaderSingleWriter() { m_cReaders = 0; InitializeCriticalSection(&m_csWrite); InitializeCriticalSection(&m_csReaderCount); m_hevReadersCleared = CreateEvent(NULL,TRUE,TRUE,NULL); } ~MultiReaderSingleWriter() { WaitForSingleObject(m_hevReadersCleared,INFINITE); CloseHandle(m_hevReadersCleared); DeleteCriticalSection(&m_csWrite); DeleteCriticalSection(&m_csReaderCount); } void EnterReader(void) { EnterCriticalSection(&m_csWrite); EnterCriticalSection(&m_csReaderCount); if (++m_cReaders == 1) ResetEvent(m_hevReadersCleared); LeaveCriticalSection(&m_csReaderCount); LeaveCriticalSection(&m_csWrite); } void LeaveReader(void) { EnterCriticalSection(&m_csReaderCount); if (--m_cReaders == 0) SetEvent(m_hevReadersCleared); LeaveCriticalSection(&m_csReaderCount); } void EnterWriter(void) { EnterCriticalSection(&m_csWrite); WaitForSingleObject(m_hevReadersCleared,INFINITE); } void LeaveWriter(void) { LeaveCriticalSection(&m_csWrite); } };

Насправді у такої реалізації є певні недоліки – наприклад вона віддає перевагу письменникам над читачами, але це мабуть найпростіше рішення яке можна легко написати.