【问题标题】:Example for boost shared_mutex (multiple reads/one write)?boost shared_mutex(多次读取/一次写入)的示例?
【发布时间】:2010-11-02 15:39:22
【问题描述】:

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

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

有没有人有一个简单的例子可以用来入门?

【问题讨论】:

标签: c++ multithreading boost mutex boost-thread


【解决方案1】:

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

【讨论】:

  • 只是评论“另一种解决方案”。当我的所有读者都是条件作者时,我所做的就是让他们总是获得一个 shared_lock,当我需要升级以写入权限时,我会 .unlock() 读者锁定并获得一个新的 unique_lock。这将使您的应用程序的逻辑复杂化,并且现在其他编写者有机会更改您第一次阅读时的状态。
  • 不应该将boost::unique_lock&lt; boost::shared_mutex &gt; lock(lock); 的行改为boost::unique_lock&lt; boost::shared_mutex &gt; lock( _access ); 吗?
  • 最后一个警告很奇怪。如果一次只能有一个线程持有upgrade_lock,那么upgrade_lock和unique_lock有什么区别?
  • @Ken 我不是很清楚,但是 upgrade_lock 的好处是,如果当前获得了一些 shared_locks,它不会阻塞(至少在升级到唯一之前不会阻塞)。但是,尝试获取 upgrade_lock 的第二个线程将阻塞,即使第一个线程没有升级到唯一的,这是我没有预料到的。
  • 这是一个已知的 boost 问题。它似乎在 boost 1.50 beta 中得到解决:svn.boost.org/trac/boost/ticket/5516
【解决方案2】:

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

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
}

【讨论】:

  • 这是我第一次使用 boost,而且我是 C++ 新手,所以也许我缺少一些东西——但在我自己的代码中,我必须指定类型,如下所示: boost::shared_lock lock(_access);
  • 我正在尝试自己使用它,但出现错误。在“锁定”之前缺少模板参数。有什么想法吗?
  • @shaz 这些是作用域的,但如果需要,您可以使用 .unlock() 提前释放它们。
  • 我已经添加了缺少的模板参数。
  • @raaj 你可以获得upgrade_lock,但是升级到唯一锁会阻塞,直到shared_lock被释放
【解决方案3】:

从 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;

【讨论】:

  • 我也想说typedef boost::unique_lock&lt; Lock &gt; WriteLock; typedef boost::shared_lock&lt; Lock &gt; ReadLock;
  • 不需要包含整个 thread.hpp 。如果您只需要锁,请包括锁。这不是内部实现。尽量减少包含。
  • 绝对是最简单的实现,但我认为将互斥锁和锁都称为锁会令人困惑。互斥体是互斥体,锁是保持它处于锁定状态的东西。
【解决方案4】:

只是为了补充一些经验信息,我一直在调查可升级锁的整个问题,Example for boost shared_mutex (multiple reads/one write)? 是一个很好的答案,添加了重要信息,即即使未升级,也只有一个线程可以拥有 upgrade_lock,即很重要,因为这意味着您不能在不先释放共享锁的情况下从共享锁升级到唯一锁。 (这已经在别处讨论过,但最有趣的线程在这里http://thread.gmane.org/gmane.comp.lib.boost.devel/214394

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

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

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

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

【讨论】:

  • Terekhov algorithm 确保在1. 中,作者不会饿死读者。见this。但是2. 是真的。 upgrade_lock 不保证公平。见this
【解决方案5】:

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

【讨论】:

  • (1) 如何让编写者原子地将计数递减任意数量? (2) 如果作者确实以某种方式将计数减至零,它如何等待已经运行的读者在写之前完成?
  • 坏主意:如果两个作家试图同时访问,你可能会出现死锁。
【解决方案6】:

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

Jim Morris 的帖子中的 (1) 似乎也与此相矛盾: Boost shared_lock. Read preferred?

#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/… 中的代码有效。
  • 它实际上在 (2) 中死锁,而不是在 (3) 中,因为 (2) 正在等待 (1) 释放它的锁。请记住:要获得唯一锁,您需要等待所有现有的共享锁完成。
  • @JonesV,即使 (2) 等待所有共享锁完成,它也不会是死锁,因为它与获取 (1) 的线程不同,如果行 (3)不存在,程序将在没有死锁的情况下完成。
猜你喜欢
  • 2012-03-12
  • 2012-07-15
  • 1970-01-01
  • 1970-01-01
  • 2013-08-27
  • 2015-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多