繁体   English   中英

C ++中的读写锁实现

[英]Read Write lock implementation in C++

我正在尝试使用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。 我可以尝试实现读写锁定的任何其他锁定类吗?

锁的类型还可以,但是与其在成员函数中创建它们,不如在成员函数内部创建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。 我已经在http://en.cppreference.com/w/cpp/thread/shared_mutex/lock上看到了使用unique_lock和shared_lock的建议,但是我不明白为什么这样,因为shared_mutex已经支持lock和lock_shared方法了?

可能是因为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。 此代码是否有可能导致写饥饿? 如果是,那我该如何避免饥饿呢?

是的,一点没错。 如果您这样做:

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

为了解决这个问题,最简单的方法是在T2T3之间插入一个小睡眠(例如nanosleep )。 您可以通过将其添加到writeFn1的底部来完成此writeFn1

其他方法可能涉及为锁创建队列。 如果线程无法获取锁,则将其自身添加到队列中,并在释放锁时队列中的第一个线程获取锁。 在linux内核中,这是针对“排队自旋锁”实现的

Q3。 我可以尝试实现读写锁定的任何其他锁定类吗?

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

或者, boost有其自己的锁。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM