在多线程上下文中安全使用映射中的引用
Safe use of a reference in a map in multi-threaded context
上下文
我有一个 class(比方说 Foo
)管理一些集中资源作为静态地图中的历史记录,具有读取它们的访问器和添加新数据的功能(有无法删除密钥):
class Foo
{
private:
static std::map<std::string,MyDataStructure> data;
public:
static const MyDataStructure& getData(const std::string& key)
{
assert(Foo::data.count(key) > 0); // Must exist
return Foo::data[key];
}
static void addData(const std::string& key, const MyDataStructure& d)
{
assert(Foo::data.count(key) == 0); // Can not already exist
Foo::data[key] = d;
}
};
为了避免并发问题,我添加了一个我这样管理的互斥量:
class Foo
{
private:
static std::map<std::string,MyDataStructure> data;
static boost::mutex mutex_data;
public:
static const MyDataStructure& getData(const std::string& key)
{
boost::mutex::scoped_lock lock(Foo::mutex_data);
assert(Foo::data.count(key) > 0); // Must exist
return Foo::data[key];
}
static void addData(const std::string& key, const MyDataStructure& d)
{
boost::mutex::scoped_lock lock(Foo::mutex_data);
assert(Foo::data.count(key) == 0); // Can not already exist
Foo::data[key] = d;
}
};
我的问题
- 我的第一个问题是关于
getData
返回的对 Foo::data
的引用:这个引用是在互斥锁的范围之外使用的,那么它可能有问题吗?是否有可能由于另一个访问添加数据而丢失引用?简而言之:地图中的引用是否始终相同?
- 如果是,
addData
中是否需要 assert
?如果我更改链接到地图中现有键的数据,参考会更改吗?
getData
需要锁吗?如果 std::map
已经是多线程安全的,我认为可能不会。
- 是否可能由于另一个访问添加数据而丢失引用?简而言之:地图中的引用是否始终相同?
见Lifetime of references in STD collections
"for std::map
, the references are valid as long as you don't clear the map, or erase the specific referenced element; inserting or erasing other elements is fine."
- 如果我更改链接到地图中现有键的数据,引用是否会更改?
根据上述规则,不...修改映射中已有的引用只是调整同一地址的位。 (只要您不通过删除密钥然后再次重新添加该密钥来实施修改。)
- getData 是否需要锁?如果 std::map 已经是多线程安全的,我认为可能不会。
见C++11 STL containers and thread safety
所以需要锁,因为映射结构的 "internal wiring" 在 addData()
的插入过程中可能会发生变化...在查找遍历 getData()
没有守卫
但是,如果您手头有参考,您可以在添加或删除过程中读取或写入已经存在的 MyDataStructure
参考之一。只是从地图到参考的导航过程需要确保在导航期间没有人在写。
上下文
我有一个 class(比方说 Foo
)管理一些集中资源作为静态地图中的历史记录,具有读取它们的访问器和添加新数据的功能(有无法删除密钥):
class Foo
{
private:
static std::map<std::string,MyDataStructure> data;
public:
static const MyDataStructure& getData(const std::string& key)
{
assert(Foo::data.count(key) > 0); // Must exist
return Foo::data[key];
}
static void addData(const std::string& key, const MyDataStructure& d)
{
assert(Foo::data.count(key) == 0); // Can not already exist
Foo::data[key] = d;
}
};
为了避免并发问题,我添加了一个我这样管理的互斥量:
class Foo
{
private:
static std::map<std::string,MyDataStructure> data;
static boost::mutex mutex_data;
public:
static const MyDataStructure& getData(const std::string& key)
{
boost::mutex::scoped_lock lock(Foo::mutex_data);
assert(Foo::data.count(key) > 0); // Must exist
return Foo::data[key];
}
static void addData(const std::string& key, const MyDataStructure& d)
{
boost::mutex::scoped_lock lock(Foo::mutex_data);
assert(Foo::data.count(key) == 0); // Can not already exist
Foo::data[key] = d;
}
};
我的问题
- 我的第一个问题是关于
getData
返回的对Foo::data
的引用:这个引用是在互斥锁的范围之外使用的,那么它可能有问题吗?是否有可能由于另一个访问添加数据而丢失引用?简而言之:地图中的引用是否始终相同? - 如果是,
addData
中是否需要assert
?如果我更改链接到地图中现有键的数据,参考会更改吗? getData
需要锁吗?如果std::map
已经是多线程安全的,我认为可能不会。
- 是否可能由于另一个访问添加数据而丢失引用?简而言之:地图中的引用是否始终相同?
见Lifetime of references in STD collections
"for
std::map
, the references are valid as long as you don't clear the map, or erase the specific referenced element; inserting or erasing other elements is fine."
- 如果我更改链接到地图中现有键的数据,引用是否会更改?
根据上述规则,不...修改映射中已有的引用只是调整同一地址的位。 (只要您不通过删除密钥然后再次重新添加该密钥来实施修改。)
- getData 是否需要锁?如果 std::map 已经是多线程安全的,我认为可能不会。
见C++11 STL containers and thread safety
所以需要锁,因为映射结构的 "internal wiring" 在 addData()
的插入过程中可能会发生变化...在查找遍历 getData()
没有守卫
但是,如果您手头有参考,您可以在添加或删除过程中读取或写入已经存在的 MyDataStructure
参考之一。只是从地图到参考的导航过程需要确保在导航期间没有人在写。