我有包含在 shared_ptr 中的资源“resource”,我想从其他线程访问它。 当我这样做时:
// foo.h
class Foo{
public:
std::shared_ptr<Setup> GetSomeThing();
void SetSomeThing();
private:
std::shared_ptr<Setup> resource;
std::mutex lock;
}
//Foo.cpp
std::shared_ptr<Setup> Foo::GetSomeThing()
{
std::lock_guard<std::mutex> lock (mutex);
return resource;
}
void Foo::SetSomeThing()
{
std::lock_guard<std::mutex> lock (mutex);
resource = ...;
}
一切都好吗? 什么时候创建返回对象,什么时候销毁锁?文档中有关于它的内容吗? 谢谢!
最佳答案
此答案假设(为清楚起见)两行:
std::lock_guard<std::mutex> lock (mutex);
都被替换为
std::lock_guard<std::mutex> guard (lock);
如果多个线程访问 std::shared_ptr<>
的单个实例没有同步和任何调用非 const
成员然后发生数据竞争。
这意味着您必须确保 SetSomeThing()
之间的同步和 GetSomething()
.
介绍 std::mutex
并使用 std::lock_guard<>
以提议的方式将做到这一点。返回的拷贝将在 guard
的析构函数之前构建被称为。
请注意,这是关于同一个实例。 GetSomeThing()
返回的拷贝具有足够的内部同步以确保它可以在不同步其他实例的情况下访问(非 const
甚至被破坏)。
然而,这些都不能阻止任何共享 Setup
上的数据竞争std::shared_ptr<Setup>
拥有的对象(部分) .如果全部访问Setup
是只读的,那么多个线程可以访问它,但是如果任何线程写入共享数据,就会发生数据竞争(没有进一步的同步,问题中没有显示)。
像 Setup
这样的对象在一个简单的应用程序中,可以在启动多个线程之前构造和初始化,并在除主线程之外的所有线程都已终止时销毁。在那种特定情况下,不需要进一步的同步,甚至提供的锁也是多余的。
这里是一个非规范的引用:
http://en.cppreference.com/w/cpp/memory/shared_ptr
请参阅开头描述的最后一段。
脚注:更改应用程序“设置”可能比简单地确保属性上不会发生数据竞争要困难得多。线程可能需要挂起采用更改或“放弃”事件。例如,考虑一个图形程序,其中屏幕分辨率在“绘制”步骤期间发生变化。它应该完成绘制并生成太大/太小的 Canvas ,还是丢弃部分绘制的 Canvas 并采用新的分辨率?设置是否以这样一种方式获得,即抽签会产生与“之前”或“之后”一致的东西,而不是一些荒谬的(可能崩溃的)混合体?
关于C++ 返回函数 lock_guard,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46400431/