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

如何在MS Concurrency::concurrent_unordered_map中存储含std::atomic的结构体?

问题:Concurrency::concurrent_unordered_map存储含std::atomic的结构体编译失败

微软的Concurrency::concurrent_unordered_map未提供emplace方法,导致无法借鉴普通std::unordered_map存储std::atomic的解决方案。现有代码因LexemData包含不可拷贝的std::atomic成员,调用insert时触发C2665编译错误,需求是在不引入额外库(如oneapi::tbb)的前提下,实现多线程安全的计数器递增。

原代码示例:

#include <atomic>
#include <string>
#include <concurrent_unordered_map.h>

struct LexemData {
    unsigned int id{};
    std::atomic<size_t> counter{};
};

int main()
{
    Concurrency::concurrent_unordered_map<std::string, LexemData> dict;

    std::string lexem("lexem");
    
    dict.insert({ lexem, LexemData {0,0} });  // Doesn't compile
}

解决方案1:存储动态分配的对象指针

既然无法直接存储不可拷贝的对象,可改用存储std::unique_ptr<LexemData>或std::shared_ptr<LexemData>,容器存储的是可拷贝/移动的指针类型,避免拷贝std::atomic成员。

修改后代码:

#include <atomic>
#include <string>
#include <memory>
#include <concurrent_unordered_map.h>

struct LexemData {
    unsigned int id{};
    std::atomic<size_t> counter{};
};

int main()
{
    Concurrency::concurrent_unordered_map<std::string, std::unique_ptr<LexemData>> dict;

    std::string lexem("lexem");
    // 构造对象后转移所有权插入容器
    auto data = std::make_unique<LexemData>(LexemData{0, 0});
    dict.insert({ lexem, std::move(data) });

    // 多线程递增计数器示例
    if (auto it = dict.find(lexem); it != dict.end()) {
        it->second->counter.fetch_add(1, std::memory_order_relaxed);
    }
}

说明:concurrent_unordered_map的insert支持移动语义,std::unique_ptr的所有权转移不会触发LexemData的拷贝;find操作本身线程安全,atomic成员的递增操作天然原子,无需额外锁。

解决方案2:利用pair的移动构造插入

std::atomic类型支持移动构造(C++11及以后),因此LexemData会自动生成移动构造函数。可以直接构造std::pair<const std::string, LexemData>并移动插入,避免拷贝操作。

修改后代码:

#include <atomic>
#include <string>
#include <concurrent_unordered_map.h>

struct LexemData {
    unsigned int id{};
    std::atomic<size_t> counter{};
};

int main()
{
    Concurrency::concurrent_unordered_map<std::string, LexemData> dict;

    std::string lexem("lexem");
    // 直接构造pair并移动插入,避免拷贝LexemData
    dict.insert(std::make_pair(std::move(lexem), LexemData{0, 0}));

    // 多线程递增计数器示例
    if (auto it = dict.find("lexem"); it != dict.end()) {
        it->second.counter.fetch_add(1, std::memory_order_relaxed);
    }
}

说明:通过std::move转移字符串和LexemData的所有权,全程不会触发std::atomic的拷贝,符合容器的插入要求。

解决方案3:自定义可拷贝的原子封装(不推荐)

如果必须存储值类型,可自定义封装std::atomic的类,通过显式拷贝构造复制原子值,但这种方式会破坏原子操作的语义,仅适用于插入操作单线程完成、后续仅做递增的场景。

修改后代码:

#include <atomic>
#include <string>
#include <concurrent_unordered_map.h>

struct CopyableAtomicSizeT {
    std::atomic<size_t> value{};

    // 拷贝构造:读取当前原子值快照
    CopyableAtomicSizeT(const CopyableAtomicSizeT& other) : value(other.value.load()) {}
    // 赋值运算符
    CopyableAtomicSizeT& operator=(const CopyableAtomicSizeT& other) {
        value.store(other.value.load());
        return *this;
    }
    // 默认保留移动构造/赋值
    CopyableAtomicSizeT(CopyableAtomicSizeT&&) = default;
    CopyableAtomicSizeT& operator=(CopyableAtomicSizeT&&) = default;
    // 封装原子递增接口
    void fetch_add(size_t n, std::memory_order order = std::memory_order_seq_cst) {
        value.fetch_add(n, order);
    }
};

struct LexemData {
    unsigned int id{};
    CopyableAtomicSizeT counter{};
};

int main()
{
    Concurrency::concurrent_unordered_map<std::string, LexemData> dict;

    std::string lexem("lexem");
    dict.insert({ lexem, LexemData {0,0} });

    // 多线程递增计数器示例
    if (auto it = dict.find(lexem); it != dict.end()) {
        it->second.counter.fetch_add(1);
    }
}

说明:拷贝操作会读取原子值的瞬时快照,多线程插入时可能导致数据不一致,仅作为特殊场景下的备选方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 01:44:54