我有一个容器
std::unordered_map<std::string, std::vector<int>> myMap;
和一个互斥体std::mutex myMutex
。
当我尝试访问 myMap
时,我使用 myMutex
,如下代码所示:
void insert(const std::string& Key)
{
std::lock_guard<std::mutex> lock(myMutex);
if(!myMap.contains(Key))
{
auto& v = myMap[Key];
// do something to v to make it not empty
}
}
std::vector<int>& get(const std::string& Key)
{
std::lock_guard<std::mutex> lock(myMutex);
static std::vector<int> empty; // this container will be read only
if(myMap.contains(Key))
{
auto& v = myMap[Key];
return v;
}
return empty;
}
如果我尝试修改容器,它是线程安全的吗
std::vector<int>& apples = get("Apple");
if(!apples.empty()) // check for existance
{
//do something to apples
}
来自一个线程(当我尝试更改myMutex
时,我
不使用
apples
),然后调用函数
insert("AnotherApple");
来自另一个线程?就我而言,我与
insert
和
get
同步,但当我对参考值(这里是容器)进行一些更改时,我不与它们同步(尤其是
insert
,它将修改 unordered_map)指定键的
apples
)从
get
获取。但我将使用其他一些互斥锁(
myMutex
除外)来与这些更改同步,以防止从不同线程写入同一键的内容。似乎在
https://stackoverflow.com/questions/25994312/can-i-access-a-c11-stdmap-entry-while-inserting-erasing-from-another-thread中,Yakk的第一个答案- Adam Nevraumont 说
您还可以修改以及
map
或set
的元素的非关键组件,同时运行其他不读/写/销毁所述元素的非关键组件的操作(这是大多数操作,除了类似的东西)erase
或=
)。
https://en.cppreference.com/w/cpp/container中的声明
都在描述我的情况。但我不太确定,有人可以给我一些澄清吗?同一容器的元素可以与那些未指定访问这些元素的成员函数同时修改。
std::mutex myMutex;
std::unordered_map<std::string, std::vector<int>> myMap;
然后
int main() {
insert("Apple");
std::jthread t1([]() {
std::vector<int>& apples = get("Apple");
if(!apples.empty()) // check for existance
{
apples.push_back(70);
}
});
std::jthread t2([]() {
insert("Banana");
});
}
不存在数据竞争(也没有所谓的逻辑竞争条件),因为由于std::unordered_map
的迭代器失效
规则,即使发生重新散列,引用在插入后仍然有效(请注意,情况并非如此)对于迭代器)。这意味着线程
t2
不会接触线程
t1
中使用的数据,除非您尝试再次插入“Apple”。顺便说一句,我宁愿在应用程序级别同步对
myMap
的访问,而不是在
insert
、
get
函数内部。这样您就可以随时决定需要哪种同步粒度级别并避免竞争条件。