You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何多线程并发写入vector<unordered_map>未出现数据损坏?

为什么多线程写入vector中的独立unordered_map不会导致数据损坏?

注:以下代码使用GCC 11.2.0编译。

我原本想复现复杂代码中的问题,编写了这个简化示例,却发现它能正常运行。现在我更关注其运行原理:代码创建了一个存储std::unordered_map的std::vector,数量与线程数一致,每个线程负责填充vector中的一个map。我原以为vector是连续内存且动态变化,多线程并发写入会导致数据损坏,但实际运行结果却符合预期,请问这是为什么?

#define _GLIBCXX_USE_NANOSLEEP  //add it top of c++ code
#include <iostream>
#include <vector>
#include <unordered_map>
#include <thread>

void add_map(std::unordered_map<int, int>& um, int map_size) {
  for (int i = 0; i < map_size; i++) {
    um.insert({i, i});
  }
}

void print_map(std::unordered_map<int, int>& um) {
  for (auto& u : um) { std::cout << u.first << " " << u.second << std::endl; }
}

int main(int argc, char* argv[]) {
  // Get number of threads from user input and set random seed
  int num_threads = std::stoi(argv[1]);
  std::srand((unsigned) time(NULL));

  // Populate a vector with num_threads maps, so each thread can write to each map inside the vector
  std::vector<std::unordered_map<int, int>> chunks;
  for (int i = 0; i < num_threads; i++) {
    std::unordered_map<int, int> empty_map;
    chunks.push_back(empty_map);
  }

  // Come up with the sizes for each map that will be handled by each thread
  std::vector<int> chunk_sizes;
  for (int i = 0; i < num_threads; i++) {
    int chunk_size = 1 + (std::rand() % 5);
    chunk_sizes.push_back(chunk_size);
  }
  std::cout << "Chunk sizes" << std::endl; for (int i = 0; i < num_threads; i++) { std::cout << chunk_sizes[i] << " "; } std::cout << std::endl;

  // For each thread, create a <int, int> unordered map so that the parts of the vector 'chunks' gets populated concurrently
  std::vector<std::thread> threads;
  for (int i = 0; i < num_threads; i++) {
    threads.push_back(std::thread(add_map, std::ref(chunks[i]), chunk_sizes[i]));
  }
  for (int i = 0; i < threads.size(); i++) { threads[i].join(); }

  // Print the maps
  for (int i = 0; i < chunks.size(); i++) {
    std::cout << "=== chunk: " << i << "; size: " << chunk_sizes[i] << " ===" << std::endl;
    print_map(chunks[i]);
  }
}

运行参数为5时的输出

Chunk sizes
2 5 5 4 4
=== chunk: 0; size: 2 ===
1 1
0 0
=== chunk: 1; size: 5 ===
4 4
3 3
2 2
1 1
0 0
=== chunk: 2; size: 5 ===
4 4
3 3
2 2
1 1
0 0
=== chunk: 3; size: 4 ===
3 3
2 2
1 1
0 0
=== chunk: 4; size: 4 ===
3 3
2 2
1 1
0 0

原因分析

  • vector的内存布局在启动线程前已固定:在创建线程之前,你已经通过循环将num_threads个空unordered_map全部添加到了chunks中,此时vector的大小和内存布局已经确定。后续线程仅修改vector中已存在的map对象,不会执行push_back、insert等可能触发vector扩容的操作——vector只有在需要新增元素时才会重新分配内存、移动现有元素,而你的代码完全不会触发这个过程,因此不存在多线程下vector扩容导致的内存冲突。

  • 线程操作的是完全独立的对象:vector中的每个unordered_map都是独立的实例,它们的存储彼此分离(vector直接存储map对象,而map内部的哈希表数据在堆上独立分配)。每个线程仅负责写入chunks[i]对应的那个map,线程之间没有交叉访问同一个对象的情况。根据C++标准,不同线程操作完全独立的对象时,不存在数据竞争,不会产生未定义行为。

  • 单线程操作unordered_map是安全的:虽然std::unordered_map本身不是线程安全的,但每个线程对自己负责的map进行的是单线程写入操作,这种场景下map的内部数据结构不会出现竞争损坏,完全符合标准规范。

内容的提问来源于stack exchange,提问作者The_Questioner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:15:33