在锁定前后检查资源

问题描述

我遇到过类似这样的简化代码

inline someClass* otherClass::getSomeClass()
{
    if (m_someClass)
        return m_someClass.get();

    std::unique_lock<std::shared_mutex> lock(m_lock);
    if (m_someClass)
        return m_someClass.get();

    m_someClass= std::make_unique<someClass>(this);
    return m_someClass.get();
}

所以这似乎是一种确保创建 someClass 对象的线程安全性的模式。我在多线程方面没有太多经验,但是这段代码对我来说并不好看。有没有其他方法可以重写它,或者它应该是一种方式?

解决方法

这里最大的问题是您违反了 C++ 内存模型。在C++内存模型中,对同一数据的写操作和读操作必须同步。

前面的 m_someClass 正在读取互斥锁中写入的内容。

operator bool 上的 m_someClass 可能以某种方式是原子的。

此外,您的代码不会处理被销毁的对象。

如果它是原子的,那么您可能应该使用原子操作来更新它而不是锁。这种模式可能会导致创建“浪费”的对象;通常这值得移除锁的成本。

使m_someClass成为std::atomic<std::shared_ptr<someClass>>

std::shared_ptr<someClass> 返回 getSomeClass

auto existing = m_someClass.load();
if (existing)
  return existing;

auto created = std::make_shared<someClass>(this);
if (
  m_someClass.compare_exchange_strong(existing,created)
) {
  return created;
} else {
  return existing;
}

如果两个线程同时尝试获取,则它们都可以创建一个新的 someClass,但只有一个会持久化,另一个会被丢弃,并且函数将返回持久化的那个。

>