将shared_lock升级为unique lock的使用,时间和设计

7

我正在更新一段代码,该代码以前使用自己的读写锁机制编写(这段代码是在C++11和std::shared_mutex出现之前编写的),现在要使用C++的std版本。

有两个类名为ReadLock和WriteLock,ReadLock可以通过方法调用升级为WriteLock。WriteLock也可以随时从shared_lock获取而不需要升级。 阅读C ++ std版本的shared_lock,我认为它很直观和易于理解。

将ReadLock替换为shared_lock 将WriteLock替换为unique_lock,并且可以通过调用lock()随时获得Writelock 升级到Writelock只需执行两个步骤:解锁shared_lock并锁定unique_lock

我的问题是,当我阅读讨论时,可能会出现特定问题,如Howard Hinnant提出的以下线程(来自Stack Overflow) Can a shared lock on a std::shared_timed_mutex be upgraded to an exclusive lock?

因此,考虑使用boost版本,因为它支持upgrade_lock和boost :: upgrade_to_unique_lock,但我对如何设计它感到困惑。

WriteLock类既可以表示unique_lock,也可以表示upgrade_lock和upgrade_to_unique_lock对象,因为正如我上面所述,WriteLock也可以在没有shared_lock的情况下获得。

此外,如果使用boost中的upgrade_lock,我不确定如何明确触发lock()和unlock()机制; boost :: upgrade_lock具有接受defer_lock参数的构造函数,并且具有可以随时使用的lock()和unlock()方法,但如果我们使用boost :: upgrade_to_unique_lock()将其升级到unique lock,则不会看到相同的功能,创建upgrade_to_unique_lock()对象会自动转换并锁定它。

以下是我要完成的假代码 方法1基本操作:

    void foo()
    {
        ReadLock readLock = someWrapper->AcquireReader();    // return a shared lock
        ...
        WriteLock writeLock = readLock->UpgradeToWriter();   // returns a unique lock
        writeLock->Lock();
        // do something here
    }   // writelock are unlock since constructor is called

方法2:直接使用写锁

    void foo()
    {
        WriteLock writeLock = someWrapper->GetWriter();   // acquire lock straight
        writeLock->Lock();
        // do something here
    }   // writelock are unlock since constructor is called
}

我的问题如下:

  1. 如果我使用std C++,是否需要使用boost,还是C++的std::shared_mutex就足够了? 在上面的示例中,UpgradeToWriter()和GetWriter()都将返回带有unique_lock对象的对象,但Upgrade将首先解锁shared_lock。
  2. 如果我使用boost版本,应该如何设计,因为upgrade_to_unique_lock是不同于unique_lock的类型? 我考虑WriteLock类包含unique_lock、upgrade_lock和boost:upgrade_to_unique_lock的定义,并且无论是升级还是普通获取,它们都将被锁定,这带我到最后一个问题
  3. 如何显式或推迟/触发boost:upgrade_to_unique_lock的锁定? 因为它没有defer_lock参数,也没有lock()和unlock()方法,只有构造函数来完成它的工作?

来自boost关于boost::upgrade_to_unique_lock使用的示例,所有示例看起来都像下面的代码,创建对象会自动触发转换和锁定。

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); // <-- want this to be performed or triggered explicitly, how?
  // now we have exclusive access
}

非常感谢您的帮助,我知道这里有C++专家可以帮助一个兄弟。


1
如果两个读者同时尝试升级他们的锁,你希望发生什么?或者你想要一个特殊的“不可升级锁”,只有一个读者可以拥有它吗? - David Schwartz
具备升级功能的健全库有3个锁:读取锁、可升级锁和写入锁。最多只能存在一个(升级/写入)锁。可以存在任意数量的读取锁和一个可升级锁。将可升级锁升级为写入锁会阻塞所有新的读取操作,并等待现有的读取操作完成,然后变成写入锁。看起来OP手写的系统没有实现这个功能?Boost似乎实现了这个功能。这是你的问题吗,OP? - Yakk - Adam Nevraumont
@DavidSchwartz 写锁应该是互斥的,所以如果两个人都试图升级,则只有一个可以通过。 我仍然希望有多个读者使用 shared_lock,如果需要切换到 unique lock ,那就使用 upgrade_lock 就行了。我不明白 "可升级锁" 的用途,如果它不会被升级为独占锁,那么它只允许一个读者,这时我真的不理解 upgrade_lock 只有一个读者的用例,请帮我理解一下? - user2300947
@user2300947 如果一个持有写锁,另一个持有读锁,该怎么办?请参考我的“答案”,虽然它实际上并不是一个真正的答案。 - David Schwartz
1个回答

8

以下是您需要了解的内容:

  1. 两个线程分别拥有可升级的读锁。
  2. 这两个线程都试图将读锁升级为写锁。
  3. 在其他所有读锁未释放之前,这两个线程都无法继续执行。
  4. 这两个线程都持有读锁。

这是一个死锁。所以您有两个选择:

  1. 确保这种情况永远不会发生:只有一个线程可以同时持有可升级的读锁。
  2. 打破死锁:当您尝试升级读锁时,结果可能是操作失败,并要求线程在尝试获取写锁之前释放读锁。

谢谢,我也阅读了http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3427.html,现在理解了update_lock的正当性。我的另一个担忧是如果我使用boost,如何通过boost::upgrade_to_unique_lock显式触发转换为unique lock。我在网上看到的大多数示例和代码都是在本地声明并自动锁定该范围。 - user2300947

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接