многопоточность — Как я могу реализовать блокировку C ++ Reader-Writer, используя один метод разблокировки, который может быть вызван как читатель или писатель?

Я работаю над проектом, который требует использования определенных абстракций ОС, и мне нужно реализовать блокировку чтения-записи, используя их семафор и мьютекс. У меня на данный момент есть настройка в формате:

class ReadWriteLock
{
public:
ReadWriteLock(uint32_t maxReaders);
~ReadWriteLock();
uint32_t GetMaxReaders() const;
eResult  GetReadLock(int32_t timeout);
eResult  GetWriteLock(int32_t timeout);
eResult  Unlock();

private:
uint32_t m_MaxReaders;
Mutex* m_WriterMutex;
Semaphore* m_ReaderSemaphore;

};

В этой реализации мне нужно использовать этот метод Unlock, чтобы либо разблокировать писатель и освободить все слоты семафора читателя, либо просто освободить слот семафора читателя, однако я изо всех сил не могу придумать реализацию, которая будет работать во всех случаев. Как я могу сделать эту работу в данной настройке? Я знаю, что это возможно, поскольку POSIX смог реализовать универсальный метод разблокировки в своей реализации, но я не могу найти никаких указаний на то, как это было сделано, поэтому был бы признателен за любую информацию, которой могут поделиться люди.

Обратите внимание, что я не могу использовать C ++ 11 или другие примитивы ОС.

3

Решение

Ну, определим две функции UnlockRead а также UnlockWrite,

Я считаю, что вам не нужны оба доступа (запись / чтение) в одно и то же время в одном и том же месте. Итак, я предлагаю два других класса для блокировки доступа:

class ReadWriteAccess
{
public:
ReadWriteAccess(uint32_t maxReaders);
~ReadWriteAccess();
uint32_t GetMaxReaders() const;
uint32_t GetMaxReaders() const;
eResult  GetReadLock(int32_t timeout);
eResult  GetWriteLock(int32_t timeout);
eResult  UnlockWrite();
eResult  UnlockRead();

private:
uint32_t m_MaxReaders;
Mutex* m_WriterMutex;
Semaphore* m_ReaderSemaphore;

};

И есть отдельные классы для чтения и записи блокировки и использования RAII быть всегда в безопасности:

class ReadLock
{
public:
ReadLock(ReadWriteAccess& access, int32_t timeout) : access(access)
{
result = access.GetReadLock(timeout);
}
eResult getResult() const { return result; }
~ReadLock()
{
if (result)
access.UnlockRead();
}
private:
ReadWriteAccess& access;
eResult  result;
};

и используйте так:

T someResource;
ReadWriteAccess someResourceGuard;

void someFunction()
{
ReadLock lock(someResourceGuard);
if (lock.getResult())
cout << someResource; // it is safe to read something from resource
}

Конечно, очень похожая реализация, которую вы можете легко написать самостоятельно для WriteLock


Поскольку OP настаивал на том, чтобы в комментариях была «одна» разблокировка — пожалуйста, учтите недостатки:

Предположим, реализован некий стек последних вызовов функций Lock:

class ReadWriteLock
{
public:
ReadWriteLock(uint32_t maxReaders);
~ReadWriteLock();
uint32_t GetMaxReaders() const;
eResult  GetReadLock(int32_t timeout)
{
eResult result = GetReadLockImpl(timestamp);
if (result)
lockStack.push(READ);
}
eResult  GetWriteLock(int32_t timeout)
{
eResult result = GetWriteLockImpl(timestamp);
if (result)
lockStack.push(WRITE);
}
eResult  Unlock()
{
LastLockMode lockMode = lockStack.top();
lockStack.pop();
if (lockMode == READ)
UnlockReadImpl();
else
UnlockWriteImpl();
}

private:
uint32_t m_MaxReaders;
Mutex* m_WriterMutex;
Semaphore* m_ReaderSemaphore;

enum Mode { READ, WRITE };
std::stack<Mode> lockStack;
};

Но вышесказанное будет работать только в однопоточном приложении. И однопоточное приложение никогда не нуждается в каких-либо блокировках.

Итак, у вас должен быть многопоточный стек, например:

template <typename Value>
class MultiThreadStack
{
public:
void push(Value)
{
stackPerThread[getThreadId()].push(value);
}
Value top()
{
return stackPerThread[getThreadId()].top();
}
void pop()
{
stackPerThread[getThreadId()].pop();
}
private:
ThreadId getThreadId() { return /* your system way to get thread id*/; }
std::map<ThreadId, std::stack<Value>> stackPerThread;
};

Так что используйте это MultiThreadStack не std :: stack в ReadWriteLock,

Но, std::map выше понадобится ReadWriteLock заблокировать доступ к нему из нескольких потоков — так что, либо вы знаете все свои потоки, прежде чем начать использовать этот материал (предварительная регистрация), либо вы столкнетесь с той же проблемой, как описано Вот. Так что мой совет — если можешь — поменяй свой дизайн.

2

Другие решения

При успешном получении блокировки тип известен: либо у вас работает много читателей, либо только один писатель, у вас не может быть и читателей, и писателей, работающих с правильно полученной блокировкой.

Поэтому достаточно сохранить текущий режим блокировки, когда lock вызов успешен и все последующие unlock вызовы (потенциально много в случае, если разрешение на чтение было предоставлено, только один действительно, если была запрошена блокировка записи) будут из этого режима.

1

По вопросам рекламы [email protected]