C++ 在无序映射(C+;+;)中并发写入不同的存储桶?
这里是C++新手。我试图在无序的映射中同时写入不同的存储桶。从搜索中可以看出,我的理解是,这应该是一个线程安全的操作。我(可能不正确)的理解基于答案和C++11标准的参考部分(特别是第2项——我的重点): 23.2.2容器数据竞赛[容器.要求.数据竞赛] < P > 1为了避免数据竞争(17.5.9),实现应考虑以下功能:起始、结束、RRADE、RADE、前部、后部、数据、查找、下限、上限、均等范围、AT和除关联或无序的关联容器之外,运算符[]、 2尽管有(17.6.5.9)的规定,但当同时修改同一序列中不同元素(向量除外)中包含的对象的内容时,实现需要避免数据竞争 3[注:对于大小大于1的向量x,x[1]=5和*x.begin()=10可以在没有数据竞争的情况下同时执行,但是x[0]=5和*x.begin()=10同时执行可能会导致数据竞争。作为一般规则的例外,对于向量C++ 在无序映射(C+;+;)中并发写入不同的存储桶?,c++,multithreading,c++11,concurrency,C++,Multithreading,C++11,Concurrency,这里是C++新手。我试图在无序的映射中同时写入不同的存储桶。从搜索中可以看出,我的理解是,这应该是一个线程安全的操作。我(可能不正确)的理解基于答案和C++11标准的参考部分(特别是第2项——我的重点): 23.2.2容器数据竞赛[容器.要求.数据竞赛] < P > 1为了避免数据竞争(17.5.9),实现应考虑以下功能:起始、结束、RRADE、RADE、前部、后部、数据、查找、下限、上限、均等范围、AT和除关联或无序的关联容器之外,运算符[]、 2尽管有(17.6.5.9)的规定,但当同时修改
auto bkt=mm->bucket(key)代码>至自动bkt=0代码>,有效地锁定了整个无序的映射容器——一切都按预期工作
#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <thread>
#define NUM_LOCKS 409
#define N 100
#define NUM_THREADS 2
using namespace std;
class SpinLock
{
public:
void lock()
{
while(lck.test_and_set(memory_order_acquire)){}
}
void unlock()
{
lck.clear(memory_order_release);
}
private:
atomic_flag lck = ATOMIC_FLAG_INIT;
};
vector<SpinLock> spinLocks(NUM_LOCKS);
void add_to_map(unordered_map<int,int> * mm, const int keyStart, const int keyEnd, const int tid){
for(int key=keyStart;key<keyEnd;++key){
auto bkt = mm->bucket(key);
//lock bucket
spinLocks[bkt].lock();
//insert pair
mm->insert({key,tid});
//unlock bucket
spinLocks[bkt].unlock();
}
}
int main() {
int Nbefore, Nafter;
thread *t = new thread[NUM_THREADS];
//create an unordered map, and reserve enough space to avoid a rehash
unordered_map<int,int> my_map;
my_map.reserve(2*NUM_THREADS*N);
//count number of buckets to make sure that a rehash didn't occur
Nbefore=my_map.bucket_count();
// Launch NUM_THREADS threads. Thread k adds keys k*N through (k+1)*N-1 to the hash table, all with associated value = k.
for(int threadID=0;threadID<NUM_THREADS;++threadID){
t[threadID]=thread(add_to_map,&my_map,threadID*N,(threadID+1)*N,threadID);
}
// Wait for the threads to finish
for(int threadID=0;threadID<NUM_THREADS;++threadID){
t[threadID].join();
}
//count number of buckets to make sure that a rehash didn't occur
Nafter=my_map.bucket_count();
cout << "Number of buckets before adding elements: " << Nbefore <<endl;
cout << "Number of buckets after adding elements: " << Nafter << " <--- same as above, so rehash didn't occur" <<endl;
//see if any keys are missing
for(int key=0;key<NUM_THREADS*N;++key){
if(!my_map.count(key)){
cout << "key " << key << " not found!" << endl;
}
}
return 0;
}
#包括
#包括
#包括
#包括
#包括
#定义NUM_锁409
#定义N 100
#定义NUM_线程2
使用名称空间std;
类自旋锁
{
公众:
无效锁()
{
while(lck.test_和_set(memory_order_acquire)){
}
无效解锁()
{
lck.清除(内存、命令和释放);
}
私人:
原子标志lck=原子标志初始化;
};
向量自旋锁(NUM_锁);
void add_to_map(无序_map*mm,常量int keyStart,常量int keyEnd,常量int tid){
for(int key=keyStart;keybucket(key);
//锁斗
自旋锁[bkt].lock();
//插入对
mm->插入({key,tid});
//解锁铲斗
自旋锁[bkt]。解锁();
}
}
int main(){
内恩贝弗尔,纳弗特;
thread*t=新线程[NUM_THREADS];
//创建一个无序的映射,并保留足够的空间以避免再次刷新
无序地图我的地图;
我的映射保留(2*NUM\u线程*N);
//计算桶的数量,以确保不会发生再次灰化
Nbefore=my_map.bucket_count();
//启动NUM_THREADS THREADS。线程k将键k*N到(k+1)*N-1添加到哈希表中,所有键的关联值均为k。
对于(int-threadID=0;threadID,容器的元素不是bucket,而是value\u type
元素
修改std
容器中的一个元素不会对其他元素产生并发影响,但修改一个bucket并不能保证这一点
添加或删除存储桶中的元素是容器上的非const
操作,该操作不在非const
操作的特殊列表中,无需同步即可安全使用。这是提问的正确方式!研究、代码、错误以及您如何修复它!大多数新用户应该阅读此内容。+1当然,正如您可能已经了解到的,您的代码有UB,因为您对insert
的调用会导致数据竞争。在实际实现中,一个简单的失败模式是在libc++中,其中insert
会增加大小计数,因此您在大小变量上存在竞争。但只要看看您自己的库实现,就会注意它不是线程安全的方式。我同意@KerrekSB的评论,即在这种情况下,insert
会导致UB(激发原始问题)。但是,我不同意Ron过于简单的说法,即写入不是线程安全的。Ron忽略了引用标准的2和3。该标准要求在同时修改容器的不同元素时避免争用。因此,如果线程同步以不在同一位置同时写入,则写入是安全的。因此看起来一个桶接一个桶的锁就足够了,但显然不行。@KerrekSB,我如何看待libc++实现?添加元素与修改元素不同。桶不是无序集合/映射的元素——元素是键/值对。你在添加元素,我看不到这方面的保证ng safe?@Tom:我认为你误解了标准中的陈述。正如Yakk所解释的,意思是你可以同时修改不同的容器元素。你永远不允许同时修改容器本身,“修改”意味着调用任何非常量成员函数(见Ron的评论).您可以整天对实施情况进行假设,但这并不能为您购买任何标准保证。
Number of buckets before adding elements: 401
Number of buckets after adding elements: 401 <--- same as above, so rehash didn't occur
key 0 not found!
key 91 not found!
key 96 not found!
key 97 not found!
key 101 not found!
key 192 not found!
key 193 not found!
key 195 not found!