2016-02-23 91 views
5

我试图使用读取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方法了? Q2302。这段代码是否有可能导致写入不足?如果是的话,我该如何避免饥饿?

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

+1

如果您观察到写入饥饿(或读取饥饿),请提交错误报告。存在确保饥饿不会发生的算法,这就是为什么在此代码中没有读写器优先级API的原因。有关此类算法的链接,请参阅http://stackoverflow.com/a/14307116/576911。下面是一篇文章链接,其中包含基本原理和部分实现:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_imp –

回答

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对象是测试中的一员,而不是局部的功能设置锁这是不行的。

Q2:写饥饿

这是不完全清楚你将如何调用读者和作家,但,是的,是有风险的:

  • 读者只要是活动的,作者被unique_lock阻止,等待互斥体在互斥模式下可用。
  • 但是,只要wrtier正在等待,新的读者可以获得对共享锁的访问,导致unique_lock进一步延迟。

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

Q3其他锁定类

不太清楚你要找什么,但我的印象中,condition_variable可能是你的兴趣。但是逻辑有点不同。

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

2

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

+0

应该读取...作为成员,功能创造他们..等 – systemcpro

+0

好的。我松了一口气。所以我会尝试用英文写作:) – systemcpro

+0

在每个成员函数内部,在堆栈'shared_lock(mylock)'上创建所需锁类型的实例。没有必要释放它,它在功能退出时超出范围时被释放。即使在例外的情况下,这也是有效的。希望这有点像适当的英语。这是一个漫长的一天:( – systemcpro

1

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的底部。

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

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

虽然不是班级,但可以使用pthread_mutex_lockpthread_mutex_unlock。这些实现递归锁。你可以添加你自己的代码来实现相当于boost::scoped_lock。你的类可以控制语义。

或者,boost有它自己的锁。