用C ++读取写锁实现

时间:2016-02-23 21:44:06

标签: c++ multithreading concurrency locking mutex

我正在尝试使用shared_mutex

在C ++中使用读/写锁
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。有没有其他锁定类我可以尝试实现读写锁?

3 个答案:

答案 0 :(得分:2)

锁的类型是可以的,但不是将它们作为成员函数创建,而是在成员函数locktype lock(mymutex)内部创建。这样,即使在例外的情况下,它们也会被销毁。

答案 1 :(得分:2)

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可能对您有用。但逻辑有点不同。

也许,您也可以通过开箱即用的方式找到解决方案:或许有一个合适的无锁数据结构,通过略微改变方法可以促进读者和作者的共存?

答案 2 :(得分:1)

  

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

可能是因为自从c ++ 11以来,unique_lock已经存在,但是shared_lock正在加入c ++ 17。此外,[可能] unique_lock可以更有效。这是[创作者] [{3}}创建的shared_lock的原始基本原理,我遵从这一点。

  

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

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

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接近于零。这是另一个线程获取锁的窗口。因为窗口很小,所以可能无法获得它。

要解决此问题,最简单的方法是在nanosleepT2之间插入一个小睡眠(例如T3)。您可以将其添加到writeFn1的底部。

其他方法可能涉及为锁创建队列。如果某个线程无法获得锁定,它会将自身添加到队列中,并且当释放锁定时,队列中的第一个线程会获得锁定。在linux内核中,这是针对&#34;排队的spinlock&#34;

实现的
  

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

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

或者,boost有自己的锁。