文章目錄
- 1.C++map為什么線程不安全?
- 2.map大量插入會有性能問題,為什么
- 3.set的應用場景
- 4.map set mutiset mutimap unordered_map unordered_set的底層實現、使用場景、優缺點
1.C++map為什么線程不安全?
其實STL中的容器都是線程不安全的,如果想要線程安全的話,就得自己加鎖。
C++ 標準庫從 C++11 開始明確規定:多個線程并發地對同一個 std::map 對象進行讀寫操作,是未定義行為(UB)——除非你自己加鎖(如 std::mutex)。
為什么 std::map 不自帶鎖?
- 性能優先
標準庫容器的設計哲學是零開銷抽象(zero-overhead abstraction)。
如果每個操作都加鎖,即使單線程程序也會被迫承擔鎖的開銷,這違背了 C++ 的設計原則。 - 靈活性更高
用戶可以根據具體場景選擇鎖的粒度(全局鎖、分段鎖、讀寫鎖等)。
標準庫提供了 std::mutex、std::shared_mutex 等工具,讓用戶自己組合。
2.map大量插入會有性能問題,為什么
std::map 在“大量插入”場景下出現性能瓶頸,并不是因為它“不會用 CPU”,而是由它的底層數據結構(紅黑樹)和接口語義共同決定的。下面把原因拆成 4 個層面,一層層展開:
① 數據結構層面:紅黑樹的 O(log n) 代價
插入 = 搜索插入點 + 樹重平衡。
每 insert/operator[] 都要從根節點一路比較到葉子,找到插入位置;隨后可能觸發 旋轉 + 顏色翻轉,這些都是 O(log n) 的 CPU 計算。
當 n 達到百萬、千萬級時,log?(n) 依舊不算小,而且每一次操作都獨立走一遍完整路徑,CPU 分支預測失敗率高、cache miss 高。
② 內存分配層面:節點級分配帶來的負擔
紅黑樹是 “節點式容器”(node-based container),每插入一個元素就至少一次 new/malloc。
大量小對象分配 →內存碎片;分配器鎖競爭(默認 glibc ptmalloc 的 global arena 鎖);cache locality 極差:節點散落在堆各處,遍歷時隨機訪問,TLB & cache 命中率低。
③ 接口語義層面:每次插入都要“唯一鍵”檢查
std::map::insert 和 operator[] 都要 先查重,保證鍵唯一。就算你事先知道鍵不會重復,也繞不開這次查找;而哈希表(unordered_map)可以通過 reserve + emplace_hint 避免重復檢查。
④ 并發層面:自帶無鎖機制 → 線程競爭放大問題
如果你在多線程環境用全局 mutex 保護 std::map,鎖粒度是整個對象;大量插入讓這把鎖成為 熱點,線程上下文切換/睡眠開銷迅速放大(之前談過的互斥鎖 vs 自旋鎖問題)。
綜合癥狀 = 計算 + 內存 + 并發 三重放大
維度 | 開銷來源 | 量級 |
---|---|---|
CPU | 紅黑樹 log n 比較 + 旋轉 | 10? 次插入時 log?(10?) ≈ 23 |
內存 | 每元素一次 new + 指針開銷 | 額外 2~3 倍內存放大 |
并發 | 全局 mutex 或分配器鎖 | 線程越多越慢 |
std::map 大量插入慢,是因為 “紅黑樹的節點級操作 + 每次 log n 計算 + 頻繁內存分配” 三者疊加;把容器換成哈希表、把一次性插入變成批量構造,或者把 map 拆掉分片,都能讓性能上一個數量級。
3.set的應用場景
需求 | 容器 |
---|---|
需要去重、有序遍歷、范圍查詢是否存在 | std::set / std::multiset |
只去重/查詢、不關心順序 | std::unordered_set (平均 O(1)) |
鍵可重復 | std::multiset 或 unordered_multiset |
極高并發讀寫 | 第三方并發 set(tbb::concurrent_hash_set, folly::F14) |
4.map set mutiset mutimap unordered_map unordered_set的底層實現、使用場景、優缺點
容器 | 底層實現 | 典型場景 | 優點 | 缺點 |
---|---|---|---|---|
map | 紅黑樹 | 鍵值對有序、范圍查詢、遍歷 | 有序、可范圍遍歷、最壞O(log n) | 內存碎片大、插入慢、cache差 |
set | 紅黑樹 | 去重+有序遍歷、排行榜 | 有序、唯一鍵、支持lower_bound | 同map缺點 |
multiset | 紅黑樹 | 允許重復鍵的有序集合 | 保留排序、可存重復 | 同map缺點 |
multimap | 紅黑樹 | 一鍵多值的有序存儲 | 一鍵多值、范圍遍歷 | 同map缺點 |
unordered_map | 哈希表 | 鍵值對無序、高速增刪查 | 平均O(1)、內存連續性好 | 無序、rehash開銷、最壞O(n) |
unordered_set | 哈希表 | 高速去重、存在性檢查 | 平均O(1)、簡單高效 | 無序、rehash開銷、最壞O(n) |