std::promise 是 C++11 引入的一个类,用于在线程之间传递异步结果(值或异常)。它通常与 std::future 配合使用,std::promise 用于设置值或异常,而 std::future 用于获取这些值或异常。
下面通过一个更直观的生产者-消费者场景,展示 std::promise 如何在线程间传递结果:
示例:生产者线程计算平方,消费者线程获取结果
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <thread>
#include <future>
#include <chrono>
// 生产者线程:计算某个数的平方,并将结果通过 promise 传递
void producer_task(std::promise<int> result_promise, int input) {
    // 模拟耗时计算
    std::this_thread::sleep_for(std::chrono::seconds(1));
    
    // 计算平方
    int square = input * input;
    
    // 将结果设置到 promise 中(传递到消费者线程)
    result_promise.set_value(square);
}
// 消费者线程:等待结果并处理
void consumer_task(std::future<int> result_future) {
    // 阻塞等待结果(从生产者线程获取)
    int result = result_future.get();
    
    // 处理结果
    std::cout << "消费者线程收到结果: " << result << std::endl;
}
int main() {
    // 1. 创建 promise 和 future
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();
    // 2. 启动生产者线程(计算 5 的平方)
    std::thread producer(producer_task, std::move(prom), 5);
    // 3. 启动消费者线程(处理结果)
    std::thread consumer(consumer_task, std::move(fut));
    // 等待线程结束
    producer.join();
    consumer.join();
    return 0;
}
        代码解释
1. 生产者线程 (producer_task)
- 输入 :接收一个 
std::promise<int>和一个整数input。 - 操作 :
- 模拟耗时计算(1秒)。
 - 计算 
input的平方。 - 将结果通过 
promise.set_value()设置到 promise 中。 
 - 作用:将计算结果传递给其他线程。
 
2. 消费者线程 (consumer_task)
- 输入 :接收一个 
std::future<int>。 - 操作 :
- 调用 
future.get()阻塞等待结果。 - 获取结果后打印。
 
 - 调用 
 - 作用:从其他线程获取结果并进行处理。
 
3. 主线程
- 创建 
std::promise<int>和关联的std::future<int>。 - 将 
promise传递给生产者线程,将future传递给消费者线程。 - 等待两个线程完成。
 
关键点
- 
线程间结果传递:
- 生产者线程通过 
promise.set_value()设置结果。 - 消费者线程通过 
future.get()获取结果。 
 - 生产者线程通过 
 - 
所有权转移:
std::promise和std::future不可复制,但可通过std::move转移所有权。- 主线程将 
promise移入生产者线程,future移入消费者线程。 
 - 
同步机制:
future.get()会阻塞消费者线程,直到生产者线程调用promise.set_value()。
 
输出结果
程序运行后输出:
消费者线程收到结果: 25
        更贴近实际的应用场景
假设生产者线程需要从网络下载数据,消费者线程需要处理数据:
            
            
              cpp
              
              
            
          
          void downloader(std::promise<std::string> data_promise) {
    // 模拟下载数据
    std::this_thread::sleep_for(std::chrono::seconds(2));
    data_promise.set_value("下载完成的数据");
}
void data_processor(std::future<std::string> data_future) {
    std::string data = data_future.get();
    std::cout << "处理数据: " << data << std::endl;
}
int main() {
    std::promise<std::string> prom;
    std::future<std::string> fut = prom.get_future();
    std::thread t1(downloader, std::move(prom));
    std::thread t2(data_processor, std::move(fut));
    t1.join();
    t2.join();
    return 0;
}
        总结
std::promise的作用:作为生产者线程的结果出口。std::future的作用:作为消费者线程的结果入口。- 适用场景:需要明确分离"结果生产者"和"结果消费者"的多线程任务。
 
那么,如果不使用std::promise的话,两个线程间如何传递数据呢?
在 C++ 中,如果不使用 std::promise 和 std::future,线程间传递数据可以通过其他同步机制实现。以下是几种常见方法:
1. 共享数据 + 互斥锁 (std::mutex)
通过共享变量传递数据,使用 std::mutex 保护共享数据,防止数据竞争。
示例:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
std::mutex mtx;
int shared_data = 0;
void producer() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::lock_guard<std::mutex> lock(mtx);
    shared_data = 42; // 生产者写入数据
}
void consumer() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "消费者收到数据: " << shared_data << std::endl;
}
int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}
        缺点:需要手动同步,容易因锁的顺序不当导致死锁。
2. 条件变量 (std::condition_variable)
通过条件变量通知其他线程数据已就绪,通常与互斥锁和共享变量配合使用。
示例:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool data_ready = false;
int shared_data = 0;
void producer() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lock(mtx);
        shared_data = 42;
        data_ready = true;
    }
    cv.notify_one(); // 通知消费者
}
void consumer() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return data_ready; }); // 等待数据就绪
    std::cout << "消费者收到数据: " << shared_data << std::endl;
}
int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}
        优点:避免忙等待,效率更高。
3. 原子操作 (std::atomic)
对简单数据类型(如 int、bool)使用原子变量,无需显式加锁。
示例:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> atomic_data(0);
void producer() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    atomic_data.store(42); // 原子写入
}
void consumer() {
    while (atomic_data.load() == 0) { // 原子读取
        std::this_thread::yield(); // 避免忙等待
    }
    std::cout << "消费者收到数据: " << atomic_data.load() << std::endl;
}
int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}
        缺点:仅适用于基本数据类型,无法处理复杂对象。
4. 消息队列(生产者-消费者模式)
使用队列作为缓冲区,生产者写入数据,消费者读取数据。需配合互斥锁和条件变量。
示例:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> msg_queue;
void producer() {
    for (int i = 0; i < 5; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        {
            std::lock_guard<std::mutex> lock(mtx);
            msg_queue.push(i);
        }
        cv.notify_one(); // 通知消费者
    }
}
void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !msg_queue.empty(); });
        int data = msg_queue.front();
        msg_queue.pop();
        std::cout << "消费者收到数据: " << data << std::endl;
        if (data == 4) break; // 结束条件
    }
}
int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}
        优点:适合处理大量数据或流式数据。
5. 回调函数(函数参数传递)
通过函数参数直接将数据传递给线程,但需注意线程的生命周期。
示例:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <thread>
#include <chrono>
void consumer(int data) {
    std::cout << "消费者收到数据: " << data << std::endl;
}
int main() {
    int data = 42;
    std::thread t(consumer, data); // 通过参数传递数据
    t.join();
    return 0;
}
        缺点:仅适用于一次性数据传递,无法异步修改。
6. 全局变量
直接通过全局变量传递数据(需谨慎,通常不推荐)。
示例:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <thread>
#include <chrono>
int global_data = 0;
void producer() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    global_data = 42;
}
void consumer() {
    while (global_data == 0) {
        std::this_thread::yield();
    }
    std::cout << "消费者收到数据: " << global_data << std::endl;
}
int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}
        缺点:不安全,易引发数据竞争。
总结
| 方法 | 适用场景 | 优点 | 缺点 | 
|---|---|---|---|
| 共享数据 + 互斥锁 | 简单数据,少量访问 | 灵活通用 | 需手动管理锁,易死锁 | 
| 条件变量 | 需要等待特定条件 | 高效,避免忙等待 | 代码复杂度较高 | 
| 原子操作 | 基本数据类型,无锁需求 | 无锁,高效 | 仅支持基本类型 | 
| 消息队列 | 大量数据或流式处理 | 扩展性强 | 实现复杂 | 
| 回调函数 | 一次性数据传递 | 简单直接 | 无法动态更新数据 | 
| 全局变量 | 快速原型开发(不推荐生产环境) | 简单 | 不安全,易引发数据竞争 | 
根据具体需求选择合适的方法。对于异步结果传递,std::promise 和 std::future 是最安全和简洁的方案,但在其他场景下,上述方法可能更灵活。