我正在使用boost:thread库将单个线程程序更改为多线程。该程序使用unordered_map作为hasp_map进行查找。我的问题是......
有一次,许多线程都会写入,而另一些线程将会读取,但不会同时读取和写入,即所有线程都将读取或者所有线程都将写入。这是线程安全的和为此设计的容器吗?如果它会,它真的会并发并提高性能吗?我需要使用一些锁定机制吗?
我在某处读到C ++标准说行为将是未定义的,但这就是全部吗?
更新:我还在考虑英特尔concurrent_hash_map。这是一个不错的选择吗?
答案 0 :(得分:45)
STL容器的设计使您可以保证:
一个。多个线程同时读取
或
B中。一个线程同时写入
写入多个线程不是上述条件之一,也是不允许的。因此,多线程写入将创建数据争用,这是未定义的行为。
您可以使用互斥锁来解决此问题。 shared_mutex(与shared_locks结合使用)特别有用,因为这种类型的互斥锁允许多个并发读取器。
http://eel.is/c++draft/res.on.data.races#3是标准的一部分,它保证了在不同线程上同时使用const函数的能力。 http://eel.is/c++draft/container.requirements.dataraces指定了一些在不同线程上安全的其他非const操作。
答案 1 :(得分:8)
这是线程安全的吗?是为此设计的容器吗?
不,标准容器不是线程安全的。
我需要使用一些锁定机制吗?
是的,你这样做。由于你正在使用提升,boost::mutex
将是一个好主意;在C ++ 11中,有std::mutex
。
我在某处读到C ++标准说行为将是未定义的,但这就是全部吗?
确实,这种行为是不确定的。我不确定你的意思是“是全部吗?”,因为未定义的行为是最糟糕的行为,而展示它的程序根据定义是不正确的。特别是,不正确的线程同步可能会导致随机崩溃和数据损坏,通常以非常难以诊断的方式进行,因此您最好不惜一切代价避免它。
更新:我还在考虑英特尔concurrent_hash_map。这是一个不错的选择吗?
听起来不错,但我自己从未使用它,所以我无法提出意见。
答案 2 :(得分:3)
现有答案涵盖要点:
另外,你应该知道:
使用较早检索的迭代器,或者指向地图中项目的引用或指针,计为读取或写入操作
在其他线程中执行的写操作可能会使指针/引用/迭代器无效进入映射,就像它们在同一个线程中完成一样,即使在尝试继续之前再次获取锁定也是如此使用它们......
答案 3 :(得分:3)
std :: unordered_map符合Container(ref http://en.cppreference.com/w/cpp/container/unordered_map)的要求。有关容器线程安全性,请参阅:http://en.cppreference.com/w/cpp/container#Thread_safety。
重点:
答案 4 :(得分:1)
访问unordered_map时,可以使用concurrent_hash_map或使用互斥锁。关于使用intel concurrent_hash_map的一个问题是你必须包含TBB,但你已经使用了boost.thread。这两个组件具有重叠的功能,因此使您的代码库变得复杂。
答案 5 :(得分:0)
std::unordered_map
非常适合某些多线程情况。
还有other concurrent maps from Intel TBB:
tbb:concurrent_hash_map
。它支持用于插入/更新的细粒度,每键锁定,这是其他哈希表所无法提供的。但是,语法稍微有些罗word。参见full sample code。推荐。tbb:concurrent_unordered_map
。本质上是同一件事,一个键/值映射。但是,它的级别低得多,并且更难使用。必须提供一个哈希器,一个相等运算符和一个分配器。即使在官方的英特尔文档中,也没有任何示例代码。不推荐。答案 6 :(得分:0)
如果您不需要unordered_map的所有功能,那么此解决方案将为您服务。它使用互斥锁来控制对内部unordered_map的访问。该解决方案支持以下方法,添加更多方法应该相当简单:
样品用量:
/* SynchronizedMap
** Functional Test
** g++ -O2 -Wall -std=c++11 test.cpp -o test
*/
#include <iostream>
#include "SynchronizedMap.h"
using namespace std;
using namespace Synchronized;
int main(int argc, char **argv) {
SynchronizedMap<int, string> activeAssociations;
activeAssociations.put({101, "red"});
activeAssociations.put({102, "blue"});
activeAssociations.put({102, "green"});
activeAssociations.put({104, "purple"});
activeAssociations.put({105, "yellow"});
activeAssociations.remove(104);
cout << ".getOrDefault(102)=" << activeAssociations.getOrDefault(102, "unknown") << "\n";
cout << ".getOrDefault(112)=" << activeAssociations.getOrDefault(112, "unknown") << "\n";
if (!activeAssociations.contains(104)) {
cout << 123 << " does not exist\n";
}
if (activeAssociations.contains(101)) {
cout << 101 << " exists\n";
}
cout << "--- associations: --\n";
for (auto e: activeAssociations.associations()) {
cout << e.first << "=" << e.second << "\n";
}
}
样本输出:
.getOrDefault(102)=green
.getOrDefault(112)=unknown
123 does not exist
101 exists
--- associations: --
105=yellow
102=green
101=red
注意1:associations()方法不适用于非常大的数据集,因为它将在创建向量列表期间锁定表。
注2:我故意没有扩展unordered_map,以防止我自己意外地使用unordered_map中尚未扩展的方法,因此可能不是线程安全的。
#pragma once
/*
* SynchronizedMap.h
* Wade Ryan 20200926
* c++11
*/
#include <unordered_map>
#include <mutex>
#include <vector>
#ifndef __SynchronizedMap__
#define __SynchronizedMap__
using namespace std;
namespace Synchronized {
template <typename KeyType, typename ValueType>
class SynchronizedMap {
private:
mutex sync;
unordered_map<KeyType, ValueType> usermap;
public:
ValueType getOrDefault(KeyType key, ValueType defaultValue) {
sync.lock();
ValueType rs;
auto value=usermap.find(key);
if (value == usermap.end()) {
rs = defaultValue;
} else {
rs = value->second;
}
sync.unlock();
return rs;
}
bool contains(KeyType key) {
sync.lock();
bool exists = (usermap.find(key) != usermap.end());
sync.unlock();
return exists;
}
void put(pair<KeyType, ValueType> nodePair) {
sync.lock();
if (usermap.find(nodePair.first) != usermap.end()) {
usermap.erase(nodePair.first);
}
usermap.insert(nodePair);
sync.unlock();
}
void remove(KeyType key) {
sync.lock();
if (usermap.find(key) != usermap.end()) {
usermap.erase(key);
}
sync.unlock();
}
vector<pair<KeyType, ValueType>> associations() {
sync.lock();
vector<pair<KeyType, ValueType>> elements;
for (auto it=usermap.begin(); it != usermap.end(); ++it) {
pair<KeyType, ValueType> element (it->first, it->second);
elements.push_back( element );
}
sync.unlock();
return elements;
}
};
}
#endif