如何使用单个解锁方法(可称为读取器或写入器)实现C++读写器锁?
How can I implement a C++ Reader-Writer lock using a single unlock method, which can be called be a reader or 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 或其他操作系统原语。
好吧,定义两个函数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 inReadWriteLock
.
但是,上面的std::map
需要ReadWriteLock
才能锁定从多个线程对它的访问 - 所以,好吧,要么你在开始使用这些东西之前知道所有线程(预注册),要么你最终会遇到与这里描述的相同的问题。所以我的建议 - 如果可以的话 - 改变你的设计。
成功获取锁时,类型是已知的:要么运行多个读取器,要么只有一个写入器,则不能同时使用有效获取的锁运行读取器和写入器。
因此,当lock
调用成功时,存储当前锁定模式就足够了,并且所有后续unlock
调用(如果提供了读取许可,则可能很多,如果请求写入锁定,则实际上只有一个)都将属于该模式。
- 用C++快速读写文件
- 如何将图像传输到c++(dll)中的缓冲区,然后在c#的缓冲区中读/写
- 如何在信号处理程序和普通函数中对全局变量进行互斥读写操作
- 在一个读写器队列中,我可以用volatile替换原子吗
- C++套接字对不读/写父/子
- 如何调试读写器锁的死锁?
- 在两个线程上读/写 64 位,无互斥/锁定/原子
- Linux 挂载使用重新挂载以允许读写
- 如何使用单个解锁方法(可称为读取器或写入器)实现C++读写器锁?
- 如何用cpp程序读写EC(嵌入式控制器)寄存器
- 快速且无锁的单写器、多读器
- MVCC实现中的无锁定读写器同步
- 单读多写器,带pthreads和锁,不带boost
- Q_PROPERTY参数化读写访问器
- boost::shared_mutex多读写器互斥
- 多读写器类的线程安全性
- 重构来自/到一个数据成员的读写访问器
- 读写迭代器- c++
- 具有条件变量的读写器锁
- 是否寄存器读/写需要临界区保护