提高boost_shared_mutex的示例(多次读取/一次写入)?


116

我有一个多线程应用程序,该应用程序必须经常读取一些数据,并偶尔更新数据。现在,互斥锁可以确保对数据的安全访问,但是这很昂贵,因为我希望多个线程能够同时读取,并且仅在需要更新时才将它们锁定(更新线程可以等待其他线程完成)。 。

我认为这是boost::shared_mutex应该做的,但是我不清楚如何使用它,也没有找到明确的例子。

有人有我可以用来入门的简单示例吗?


1800 INFORMATION的示例是正确的。另请参见本文:Boost Threads中的新增功能
阿萨夫·拉维(

Answers:


102

看来您将执行以下操作:

boost::shared_mutex _access;
void reader()
{
  // get shared access
  boost::shared_lock<boost::shared_mutex> lock(_access);

  // now we have shared access
}

void writer()
{
  // get upgradable access
  boost::upgrade_lock<boost::shared_mutex> lock(_access);

  // get exclusive access
  boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
  // now we have exclusive access
}

7
这是我第一次使用boost,我是C ++新手,所以也许我缺少一些东西-但在我自己的代码中,我必须指定类型,例如:boost :: shared_lock <shared_mutex> lock (_访问);
肯·史密斯

2
我正在尝试自己使用此功能,但出现错误。在“锁定”之前缺少模板参数。有任何想法吗?
马特2010年

2
@shaz它们是作用域的,但是如果需要,您可以使用.unlock()提前释放它们。
mmocny,2011年

4
我添加了缺少的模板参数。

1
@raaj可以获取upgrade_lock,但是升级到唯一锁将一直阻塞,直到shared_lock释放为止
1800信息

166

1800信息或多或少是正确的,但是我想纠正一些问题。

boost::shared_mutex _access;
void reader()
{
  boost::shared_lock< boost::shared_mutex > lock(_access);
  // do work here, without anyone having exclusive access
}

void conditional_writer()
{
  boost::upgrade_lock< boost::shared_mutex > lock(_access);
  // do work here, without anyone having exclusive access

  if (something) {
    boost::upgrade_to_unique_lock< boost::shared_mutex > uniqueLock(lock);
    // do work here, but now you have exclusive access
  }

  // do more work here, without anyone having exclusive access
}

void unconditional_writer()
{
  boost::unique_lock< boost::shared_mutex > lock(_access);
  // do work here, with exclusive access
}

另请注意,与shared_lock不同,即使没有升级,单个线程也只能一次获得一个upgrade_lock(我认为遇到此问题时很尴尬)。因此,如果您所有的读者都是有条件的作家,则需要寻找其他解决方案。


1
只是评论“另一种解决方案”。当我所有的读者都是有条件的作家时,我所做的就是让他们始终获得shared_lock,而当我需要升级以编写特权时,我将.unlock()读者锁定并获得一个新的unique_lock。这将使应用程序的逻辑复杂化,现在其他作者有机会改变第一次阅读时的状态。
mmocny 2011年

8
该行不应该boost::unique_lock< boost::shared_mutex > lock(lock);读取boost::unique_lock< boost::shared_mutex > lock( _access );吗?
SteveWilkinson

4
最后的警告很奇怪。如果一次只有一个线程可以持有upgrade_lock,那么upgrade_lock和unique_lock有什么区别?
肯·史密斯,

2
@Ken我不是很清楚,但是upgrade_lock的好处是,如果当前有一些shared_locks被获取,它不会阻塞(至少直到您升级到unique时才如此)。但是,即使第一个线程没有升级到我以前没有想到的唯一线程,第二个尝试获取upgrade_lock的线程也会阻塞。
mmocny 2011年

6
这是一个已知的增强问题。它似乎可以在1.50 Beta时得到解决:svn.boost.org/trac/boost/ticket/5516
Ofek Shilon

47

从C ++ 17(VS2015)开始,您可以将标准用于读写锁定:

#include <shared_mutex>

typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock > WriteLock;
typedef std::shared_lock< Lock > ReadLock;

Lock myLock;


void ReadFunction()
{
    ReadLock r_lock(myLock);
    //Do reader stuff
}

void WriteFunction()
{
     WriteLock w_lock(myLock);
     //Do writer stuff
}

对于较旧的版本,可以使用具有相同语法的boost:

#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock >  WriteLock;
typedef boost::shared_lock< Lock >  ReadLock;

5
我也要说typedef boost::unique_lock< Lock > WriteLock; typedef boost::shared_lock< Lock > ReadLock;
葡萄树2012年

6
不需要包括整个thread.hpp。如果只需要锁,请包括锁。这不是内部实现。尽量减少包含。
Yochai Timmer 2014年

5
绝对是最简单的实现,但我认为将互斥体和锁都称为“锁”会令人困惑。互斥锁是一种互斥锁,锁是一种将其保持在锁定状态的东西。
Tim MB

17

只是为了增加一些经验信息,我一直在研究整个可升级锁的问题,还有用于boost shared_mutex(多次读取/一次写入)的示例?添加重要信息,即即使不升级也只有一个线程可以具有upgrade_lock,这是一个很好的答案,这一点很重要,因为这意味着您必须先释放共享锁才能从共享锁升级到唯一锁。(这已经在其他地方进行了讨论,但是最有趣的线程在这里http://thread.gmane.org/gmane.comp.lib.boost.devel/214394

但是,我确实发现了等待升级到锁的线程(即需要等待所有读取器释放共享锁的线程)和写入器锁在等待同一件事(即unique_lock)之间的重要区别(未记录)。

  1. 等待shared_mutex上的unique_lock的线程会阻止任何新的读取器进入,他们必须等待写入器的请求。这样可以确保读者不会饿死作家(但是我相信作家会饿死读者)。

  2. 等待upgradeable_lock升级的线程允许其他线程获得共享锁,因此如果读者非常频繁,则可能会饿死该线程。

这是要考虑的重要问题,可能应该记录在案。


3
Terekhov algorithm确保了1.,作家不能饿死读者。看到这个。但这2.是真的。upgrade_lock不保证公平。看到这个
JonasVautherin 2014年

2

使用计数等于读取器数量的信号量。让每个读者对信号量进行一次计数才能阅读,这样他们就可以同时阅读所有内容。然后让编写者在编写之前记录所有信号量。这导致写入器等待所有读取完成,然后在写入时阻止读取。


(1)如何使作家原子地递减计数?(2)如果编写者确实以某种方式将计数减为零,它如何等待已经运行的读者完成写操作?
Ofek Shilon 2012年

坏主意:如果两个编写者尝试同时访问,则可能会出现死锁。
Caduchon

2

吉姆·莫里斯(Jim Morris)做出了很大的回应,我偶然发现了这一点,花了我一段时间才弄清楚。以下是一些简单的代码,这些代码显示在提交“ unique_lock boost”(版本1.54)“请求”后,将阻止所有shared_lock请求。这很有趣,因为在我看来,在unique_lock和upgradeable_lock之间进行选择可以允许我们要写入优先级还是不优先。

同样,吉姆·莫里斯(Jim Morris)的帖子中的(1)似乎与此矛盾: 提高shared_lock。阅读首选?

#include <iostream>
#include <boost/thread.hpp>

using namespace std;

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > UniqueLock;
typedef boost::shared_lock< Lock > SharedLock;

Lock tempLock;

void main2() {
    cout << "10" << endl;
    UniqueLock lock2(tempLock); // (2) queue for a unique lock
    cout << "11" << endl;
    boost::this_thread::sleep(boost::posix_time::seconds(1));
    lock2.unlock();
}

void main() {
    cout << "1" << endl;
    SharedLock lock1(tempLock); // (1) aquire a shared lock
    cout << "2" << endl;
    boost::thread tempThread(main2);
    cout << "3" << endl;
    boost::this_thread::sleep(boost::posix_time::seconds(3));
    cout << "4" << endl;
    SharedLock lock3(tempLock); // (3) try getting antoher shared lock, deadlock here
    cout << "5" << endl;
    lock1.unlock();
    lock3.unlock();
}

我实际上很难弄清楚为什么[ stackoverflow.com/questions/12082405/…中的代码正常工作时,以上代码会死锁。
dale1209

1
实际上,它在(2)中死锁,而不是在(3)中死锁,因为(2)正在等待(1)释放其锁。请记住:要获得唯一锁,您需要等待所有现有共享锁完成。
JonasVautherin 2014年

@JonesV,即使(2)等待所有共享锁完成,也不会造成死锁,因为它是与获取(1)的线程不同的线程,如果第(3)行不存在,程序将没有死锁。
SagiLow
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.