如何使用单个解锁方法实现 C++ Reader-Writer 锁,它可以称为 reader 或 writer?
How can I implement a C++ Reader-Writer lock using a single unlock method, which can be called be a reader or writer?
我正在做一个项目,它需要使用特定的 OS 抽象,我需要使用它们的信号量和互斥锁来实现 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 方法来解锁写入器并释放所有 reader 信号量槽,或者简单地释放一个 reader 信号量槽,但是,我正在努力我想不出一个在所有情况下都适用的实现。我怎样才能在给定的设置中完成这项工作?我知道这是可能的,因为 POSIX 能够在他们的实现中实现一个通用的解锁方法,但我找不到任何关于如何完成的指示,所以希望人们能分享任何信息。
请注意,我不能使用 C++11 或其他 OS 原语。
好吧,定义两个函数UnlockRead
和UnlockWrite
。
我相信您不需要在同一个地方同时访问这两个 (Write/Read)。所以我的建议是另外两个 类 用于锁定访问:
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在评论中坚持要"one"解锁-请考虑缺点:
假设它实现了某种最后调用 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
来锁定从多个线程访问它 - 所以,好吧,要么你知道你所有的线程在你开始使用这些东西之前(预注册),否则你最终会遇到与 here 描述的相同的问题。所以我的建议 - 如果可以的话 - 改变你的设计。
当成功获取锁时,类型是已知的:要么你有很多读者运行,要么只有一个写者,你不能同时拥有读者和写者运行一个有效获取的锁。
因此,当 lock
调用成功以及所有后续 unlock
调用时,存储当前锁定模式就足够了(如果提供了读取许可,则可能有很多,如果请求写入锁定,则只有一个) 将是那种模式。
我正在做一个项目,它需要使用特定的 OS 抽象,我需要使用它们的信号量和互斥锁来实现 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 方法来解锁写入器并释放所有 reader 信号量槽,或者简单地释放一个 reader 信号量槽,但是,我正在努力我想不出一个在所有情况下都适用的实现。我怎样才能在给定的设置中完成这项工作?我知道这是可能的,因为 POSIX 能够在他们的实现中实现一个通用的解锁方法,但我找不到任何关于如何完成的指示,所以希望人们能分享任何信息。
请注意,我不能使用 C++11 或其他 OS 原语。
好吧,定义两个函数UnlockRead
和UnlockWrite
。
我相信您不需要在同一个地方同时访问这两个 (Write/Read)。所以我的建议是另外两个 类 用于锁定访问:
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在评论中坚持要"one"解锁-请考虑缺点:
假设它实现了某种最后调用 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
来锁定从多个线程访问它 - 所以,好吧,要么你知道你所有的线程在你开始使用这些东西之前(预注册),否则你最终会遇到与 here 描述的相同的问题。所以我的建议 - 如果可以的话 - 改变你的设计。
当成功获取锁时,类型是已知的:要么你有很多读者运行,要么只有一个写者,你不能同时拥有读者和写者运行一个有效获取的锁。
因此,当 lock
调用成功以及所有后续 unlock
调用时,存储当前锁定模式就足够了(如果提供了读取许可,则可能有很多,如果请求写入锁定,则只有一个) 将是那种模式。