C++中读写锁的实现

Read Write lock implementation in C++

我正在尝试使用 shared_mutex

在 C++ 中使用 read/write 锁
typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock >  WriteLock;
typedef boost::shared_lock< Lock >  ReadLock;

class Test {
    Lock lock;
    WriteLock writeLock;
    ReadLock readLock;

    Test() : writeLock(lock), readLock(lock) {}

    readFn1() {
        readLock.lock();
        /*
             Some Code
        */
        readLock.unlock();
    }

    readFn2() {
        readLock.lock();
        /*
             Some Code
        */
        readLock.unlock();
    }

    writeFn1() {
        writeLock.lock();
        /*
             Some Code
        */
        writeLock.unlock();
    }

    writeFn2() {
        writeLock.lock();
        /*
             Some Code
        */
        writeLock.unlock();
    }
}

代码似乎运行良好,但我有一些概念性问题。

Q1。我在http://en.cppreference.com/w/cpp/thread/shared_mutex/lock上看到了使用unique_lock和shared_lock的建议,但我不明白为什么因为shared_mutex已经支持lock和lock_shared方法?

Q2。这段代码是否有可能导致写饥饿?如果是那么我怎样才能避免饥饿?

Q3。有没有其他的锁class我可以尝试实现读写锁?

锁的类型没问题,但不是将它们作为成员函数,而是在成员函数内部创建然后 locktype lock(mymutex)。这样,即使在出现异常的情况下,它们也会在销毁时被释放。

Q1:互斥包装器的使用

建议使用包装器对象而不是直接管理互斥体是为了避免代码被中断且互斥体未释放的不幸情况,使其永远处于锁定状态。

这就是RAII的原理。

但这只有在您的 ReadLock 或 WriteLock 是使用它的函数的本地时才有效。

示例:

readFn1() {
    boost::unique_lock< Lock > rl(lock);  
    /*
         Some Code 
         ==> imagine exception is thrown
    */
    rl.unlock();   // this is never reached if exception thrown 
}  // fortunately local object are destroyed automatically in case 
   // an excpetion makes you leave the function prematurely      

在您的代码中,如果其中一个函数被中断,这将不起作用,因为您的 ReadLock WriteLock 对象是 Test 的成员,而不是设置锁的函数的本地对象。

Q2:写饿

尚不完全清楚您将如何调用读者和作者,但是,是的,存在风险:

  • 只要读取器处于活动状态,写入器就会被 unique_lock 阻塞,等待互斥量以独占模式获取。
  • 但是只要wrtier在等待,新的读者就可以获得共享锁的访问权限,导致unique_lock进一步延迟。

如果你想避免饥饿,你必须确保等待的作家有机会设置他们的unique_lock。例如,在你的读者中添加一些代码来检查作者是否在设置锁之前等待。

Q3其他锁定类

不太确定您要找什么,但我觉得 condition_variable 可能会让您感兴趣。但是逻辑有点不同。

也许,您也可以跳出框框找到解决方案:也许有一种合适的无锁数据结构可以通过稍微改变方法来促进读者和作者的共存?

Q1. I have seen the recommendations to use unique_lock and shared_lock on http://en.cppreference.com/w/cpp/thread/shared_mutex/lock, but I don't understand why because shared_mutex already supports lock and lock_shared methods?

可能是因为 unique_lock 自 c++11 以来就已存在,但 shared_lock 随 c++17 一起出现。此外,[可能] unique_lock 可以更有效率。这是 shared_lock [由创作者] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html 的原始理由,我尊重它。

Q2. Does this code have the potential to cause write starvation? If yes then how can I avoid the starvation?

是的,绝对是。如果你这样做:

while (1)
    writeFn1();

你可以得到一个时间线:

T1: writeLock.lock()
T2: writeLock.unlock()

T3: writeLock.lock()
T4: writeLock.unlock()

T5: writeLock.lock()
T6: writeLock.unlock()

...

差异 T2-T1 是任意的,取决于完成的工作量。但是,T3-T2 接近于零。这是另一个线程获取锁的window。因为window太小了,估计拿不到

要解决这个问题,最简单的方法就是在T2T3之间插入一个小睡眠(例如nanosleep)。您可以通过将其添加到 writeFn1.

的底部来执行此操作

其他方法可能涉及为锁创建队列。如果一个线程无法获得锁,它会将自己添加到一个队列中,当锁被释放时,队列中的第一个线程将获得锁。在 linux 内核中,这是为 "queued spinlock"

实现的

Q3. Is there any other locking class I can try to implement read write lock?

虽然不是 class,但您可以使用 pthread_mutex_lockpthread_mutex_unlock。这些实现递归锁。您可以添加自己的代码来实现 boost::scoped_lock 的等效项。您的 class 可以控制语义。

或者,boost有自己的锁。