如何使用单个解锁方法实现C ++ Reader-Writer锁定,可以称为读取器或写入器?

时间:2017-03-24 15:10:55

标签: c++ multithreading readwritelock readerwriterlock

我正在处理一个需要使用特定操作系统抽象的项目,我需要使用他们的信号量和互斥锁实现一个读写器锁。我目前有一个格式设置:

$var = $var.replace(/([~!@#$%^&*()-_+=`{}\[\]\|\\:;'<>,.\/? ])+/g, '-').replace(/^(-)+|(-)+$/g,'');

在这个实现中,我需要使用这种解锁方法解锁编写器并释放所有读取器信号量插槽,或者简单地释放读取器信号量插槽,但是,我正在努力,因为我无法想到一个实现,这将是在所有情况下工作。如何在给定的设置中完成这项工作?我知道POSIX能够在其实现中实现通用解锁方法是可能的,但是我找不到任何关于如何完成的指示,所以会欣赏人们可以共享的任何信息。

请注意,我不能使用C ++ 11或其他操作系统原语。

2 个答案:

答案 0 :(得分: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坚持在评论中有“一个”解锁 - 请考虑缺点:

假设它实现了某种最后一次锁定函数的堆栈:

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中使用这个ReadWriteLock而不是std :: stack。

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

答案 1 :(得分:1)

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

因此,当#include<stdio.h> #include<stdlib.h> #define MAX 100 int main() { int array*,i,n; printf("Enter size of array:\n"); scanf("%d",&n); array = malloc(n*sizeof(int)); /* code to enter numbers in array */ array += n; //remember to free pointers after you are done with them free(array); return 0; } 调用成功并且所有后续lock调用(在提供读取许可的情况下可能很多,只有在请求写入锁定时才有一个)时,存储当前锁定模式就足够了属于那种模式。