3

我正在开发一个项目,该项目需要使用特定的操作系统抽象,并且我需要使用它们的信号量和互斥锁来实现读写器锁。我目前有以下格式的设置:

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 或其他操作系统原语。

4

2 回答 2

2

好吧,定义两个函数UnlockReadUnlockWrite.

我相信您不需要在同一个地方同时进行两种访问(写/读)。所以我提议的是另外两个用于锁定访问的类:

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 in ReadWriteLock

但是std::map上面需要ReadWriteLock锁定从多个线程对它的访问 - 所以,好吧,要么你在开始使用这些东西(预注册)之前知道你所有的线程,要么你最终遇到与这里描述的相同的问题。所以我的建议——如果可以的话——改变你的设计。

于 2017-03-24T15:48:33.903 回答
1

当成功获取锁时,类型是已知的:要么有许多读取器在运行,要么只有一个写入器,您不能同时让读取器和写入器都使用有效获取的锁运行。

因此,当lock调用成功时存储当前锁定模式就足够了,并且所有后续unlock调用(如果提供了读取许可,可能会有很多调用,如果请求写锁定,则确实只有一个调用)将是该模式。

于 2017-03-24T16:52:01.473 回答