118

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

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

有没有人有一个简单的例子可以用来开始?

4

6 回答 6

174

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

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,即使它没有升级(当我遇到它时我认为这很尴尬)。所以,如果你所有的读者都是有条件的作家,你需要找到另一个解决方案。

于 2011-01-27T18:45:28.377 回答
104

看起来你会做这样的事情:

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
}
于 2009-06-13T02:53:32.883 回答
49

从 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;
于 2011-06-23T07:15:07.580 回答
18

只是为了添加更多经验信息,我一直在研究可升级锁的整个问题,以及boost shared_mutex 的示例(多次读取/一次写入)?是一个很好的答案,添加了重要信息,即即使未升级,也只有一个线程可以拥有 upgrade_lock,这很重要,因为这意味着您无法在不先释放共享锁的情况下从共享锁升级到唯一锁。(这已在其他地方讨论过,但最有趣的线程在这里http://thread.gmane.org/gmane.comp.lib.boost.devel/214394

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

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

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

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

于 2011-03-14T22:01:57.870 回答
2

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

于 2010-11-20T07:31:44.853 回答
2

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

Jim Morris 的帖子中的 (1) 也似乎与此相矛盾: Boost 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();
}
于 2013-11-20T21:34:14.767 回答