[英]How can I implement a C++ Reader-Writer lock using a single unlock method, which can be called be a reader or writer?
我正在开发一个项目,它需要使用特定的OS抽象,我需要使用他们的信号量和互斥量来实现读写器锁。 我目前有一个格式的设置:
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;
};
在这个实现中,我需要使用这种解锁方法来解锁编写器并释放所有读取器信号量插槽,或者简单地释放读取器信号量插槽,但是,我正在努力,因为我无法想到一个实现,这将在所有案例。 如何在给定的设置中完成这项工作? 我知道POSIX能够在其实现中实现通用解锁方法是可能的,但是我找不到任何关于如何完成的指示,所以会欣赏人们可以共享的任何信息。
请注意,我不能使用C ++ 11或其他OS原语。
好吧,定义两个函数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;
};
所以在ReadWriteLock
使用这个MultiThreadStack
而不是std :: stack。
但是 ,上面的std::map
需要ReadWriteLock
来锁定来自多个线程的对它的访问 - 所以,你可以在开始使用这些东西之前知道你所有的线程(预注册),或者你最终遇到与此处描述的相同的问题。 所以我的建议 - 如果可以 - 改变你的设计。
成功获取锁定时,类型是已知的:要么您有许多读者正在运行,要么只有一个作者,您不能让读者和作者都使用有效获取的锁定运行。
因此,当lock
调用成功并且所有后续unlock
调用(在提供读取许可的情况下可能很多,如果请求写入锁定时确实只有一个)将存储当前锁定模式就足够了将是该模式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.