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

C++单任务多Promise场景:多线程异步数据获取方案咨询

多线程共享异步数据请求的优化实现方案

你的场景很典型:多线程重复请求同一份key对应的数据,每个调用者需要拿到std::future自行等待结果,同时要避免对同一个key发起重复的服务端请求。你提到的“每个key维护promise队列”的方案是可行的,但确实有更简洁高效的优化思路——用std::shared_future替代promise队列,每个key仅需一个std::promise,所有请求共享它对应的shared_future。

核心思路

  1. 内部维护一个线程安全的映射表,记录每个key的请求状态:要么已完成(存储就绪的shared_future),要么正在请求中(存储对应promise生成的shared_future)。
  2. 当线程调用get_data时:
    • 如果key已有就绪的shared_future,直接返回其拷贝;
    • 如果key正在请求中,返回当前的shared_future;
    • 如果key还未发起请求,创建promise、启动异步请求,并将promise的future转换为shared_future返回,同时记录到映射表中。
  3. 数据获取完成后,通过唯一的promise设置结果,所有持有该shared_future的调用者都会被唤醒并拿到数据。

完整代码实现

#include <future>
#include <map>
#include <mutex>
#include <memory>

// 自定义的key和data类型,可根据实际业务调整
using key = int;
struct data {
    // 示例字段,替换为实际数据结构
    int value;
};

class my_thousand_promises {
public:
    std::future<data> get_data(key k) {
        std::lock_guard<std::mutex> lock(mtx_);
        
        auto iter = key_futures_.find(k);
        if (iter != key_futures_.end()) {
            // 已有请求(不管是处理中还是已完成),直接返回shared_future的拷贝
            return iter->second;
        }

        // 首次请求,创建promise并启动异步任务
        auto prom_ptr = std::make_shared<std::promise<data>>();
        std::shared_future<data> shared_fut = prom_ptr->get_future().share();
        
        // 记录到映射表,供后续请求复用
        key_futures_.emplace(k, shared_fut);

        // 启动异步数据获取(实际场景替换为真实的服务端异步调用)
        std::async(std::launch::async, [this, k, prom_ptr]() {
            // 模拟服务端数据获取
            data server_data = fetch_from_server(k);
            // 数据就绪后更新promise
            this->handle_data_received(k, std::move(server_data), prom_ptr);
        });

        return shared_fut;
    }

private:
    void handle_data_received(key k, data received_data, std::shared_ptr<std::promise<data>> prom_ptr) {
        // 设置promise结果,所有持有对应shared_future的线程都会被唤醒
        prom_ptr->set_value(std::move(received_data));

        // 可选:如果不需要长期缓存数据,可以在这里从key_futures_中删除该key
        // std::lock_guard<std::mutex> lock(mtx_);
        // key_futures_.erase(k);
    }

    // 模拟从服务端异步获取数据的函数
    data fetch_from_server(key k) {
        // 实际逻辑:发起网络请求、等待响应等
        return data{k * 10}; // 示例返回值
    }

    std::mutex mtx_;
    // 存储每个key对应的shared_future,线程安全访问
    std::map<key, std::shared_future<data>> key_futures_;
};

方案优势

  • 资源高效:每个key仅需一个promise,避免维护大量promise队列,减少内存占用和操作开销。
  • 逻辑简洁:shared_future天然支持多线程共享,无需手动遍历队列兑现每个promise。
  • 避免重复请求:同一个key只会发起一次服务端请求,节省服务端资源和网络带宽。
  • 线程安全:通过全局std::mutex保证映射表的并发访问安全(高并发场景可优化为分片锁进一步降低竞争)。

扩展注意事项

  • 异常处理:如果服务端请求失败,可在handle_data_received中调用prom_ptr->set_exception(),让所有future的调用方捕获异常。
  • 数据缓存策略:如果数据需要过期失效,可以在映射表中存储包含过期时间的结构体,定期清理过期条目。
  • 锁优化:当key数量极大且并发请求频繁时,可将映射表按key的哈希值分片,每个分片对应一个mutex,减少锁竞争。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:53:11