保持 std::mutex 锁定/解锁公开的基本原理

Rationale to keep std::mutex lock/unlock public

本文关键字:解锁 mutex std 锁定 保持      更新时间:2023-10-16

我的问题很简单。在 C++11 年,我们有std::mutexstd::lock_guardstd::unique_lock.

使用这些类的常用方法是通过任何锁锁定std::mutex。这可以防止由于异常引发而导致互斥锁泄漏:

{
   std::lock_guard<std::mutex> l(some_mutex);
   //Cannot leak mutex.
}

为什么std::mutex::lockstd::mutex::unlock是公开的?这需要不正确的使用:

{ 
     some_mutex.lock();
     //Mutex leaked due to exception.
     some_mutex.unlock();
}

std::lock_guardstd::unique_lock成为std::mutex的朋友并将std::mutex锁定操作设为私有不是更安全吗?这将防止不安全的使用。

我能猜到这个设计的唯一原因是,如果我有自己的锁,它就不能与std::mutex一起使用,因为我将无法std::mutex成为自己的锁朋友。这是公开这些成员函数的主要原因吗?

N2406 中记录了此设计决策的基本原理:

与 boost 不同,互斥体具有 lock() 的公共成员函数, 解锁(),等等。这对于支持主要目标之一是必要的: 用户定义的互斥锁可以与标准定义的锁一起使用。如果有 如果没有用户定义的互斥锁要实现的接口,则 标准定义的锁无法与用户通信 定义的互斥锁。

在撰写本文时,boost::mutex 只能使用 boost::scoped_lock 锁定和解锁。

所以你现在可以写my::mutex,只要你提供成员lock()unlock(),你的my::mutexstd::mutex一样是一等公民。 您的客户可以使用std::unique_lock<my::mutex>就像他们可以使用std::unique_lock<std::mutex>一样容易,即使std::unique_lock不知道my::mutex是什么。

一个激励人心的现实生活中my::mutex的例子是现在在C++1y(我们希望y == 4)标准草案中提出的std::shared_mutexstd::shared_mutex具有用于独占模式锁定和解锁的成员lock()unlock()。 当客户端按预期使用std::unique_lock<std::shared_mutex>时,它会与std::unique_lock交互。

以防万一您可能认为shared_mutex可能是唯一可以使用此通用接口的其他激励互斥锁,这是另一个真实世界的示例::-)

template <class L0, class L1>
void
lock(L0& l0, L1& l1)
{
    while (true)
    {
        {
            unique_lock<L0> u0(l0);
            if (l1.try_lock())
            {
                u0.release();
                break;
            }
        }
        this_thread::yield();
        {
            unique_lock<L1> u1(l1);
            if (l0.try_lock())
            {
                u1.release();
                break;
            }
        }
        this_thread::yield();
    }
}

这是如何一次锁定(以异常安全的方式)两个 BasicLockable,而不会有死锁危险的基本代码。 尽管有许多相反的批评,但这段代码非常有效。

请注意上面代码中的行:

            unique_lock<L0> u0(l0);

该算法不知道L0是什么类型。 但只要它支持lock()unlock()(好吧,也try_lock()),一切都很酷。 L0甚至可能是unique_lock的另一个实例,也许是unique_lock<my::mutex>,甚至可能是unique_lock<std::shared_mutex>,甚至可能是std::shared_lock<std::shared_mutex>

这一切都是有效的。 而这一切都是因为除了商定的lock()unlock()的公共界面之外,std::unique_lockstd::mutex之间没有亲密关系。

正如Jerry Coffin指出的那样,我们只能在没有实际委员会成员参与的情况下进行推测,但我的猜测是,他们设计的界面具有灵活性,并且能够以自己的方式扩展它们。 与其让每个潜在的类都成为std::mutex的朋友,不如公开接口来执行所需的操作,允许某人(例如提升贡献者)出现并找到一种新的更好的方法来使用它。 如果接口被隐藏,那是不可能的。