并发编程std::promise的使用及注意点

本文通过两个示例详细介绍了C++中promise的基本用法及其注意事项,包括如何正确地在不同线程间传递并设置值,以及如何避免常见的异常情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

##### 注意:正确的使用promise要必须知道它什么时候会抛出异常

enum class future_errc {
    broken_promise             = /* promise已被析构*/,
    future_already_retrieved   = /* get_future()多次 */,
    promise_already_satisfied  = /* set_value()多次 */,
    no_state                   = /* future.get()已经成功,后面又future.get()了一次 */
};


>**example:**
 

#include <iostream>
#include <memory>
#include <thread>
#include <future>
#include <mutex>
#include <chrono>
#include <string>
static std::string  future_string[] =
{ "ready",
   "timeout",
   "deferred"
};

int main()
{
    auto func = [](std::shared_ptr<std::promise<int>> ptr) {
        std::cout << "[thread]" << ptr.use_count() << std::endl;
        std::cout << "[thread] will set_value after 4s" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(4));
        if (ptr != nullptr)
        {
            ptr->set_value(20);
            std::cout << "[thread] set value done111~ " << ptr.use_count() << std::endl;
            ptr = nullptr;
            std::cout << "[thread] set value done222~ " << ptr.use_count() << std::endl;
        }
    };

    std::thread th_;

    {
        //构建promise
        std::shared_ptr<std::promise<int>> sp = std::make_shared<std::promise<int>>();
        std::future<int> future = sp->get_future();
        std::future_status status;

        //创建线程, 以智能指针方式传递
        //以普通指针传递会有特殊情况,主线程等待超时都没等到函数退出
        //就会析构promise,此后线程中调用promise->setValue()就会报错
        th_ = std::thread(func, sp);

        try
        {
            //设置10s内轮询查看当前future状态
            for (int i = 0; i < 10; ++i)
            {
                status = future.wait_for(std::chrono::seconds(1));
                std::cout << "[main] ---> current status:  " << future_string[static_cast<int>(status)] << "\t" << i << "s" << std::endl;
                if (status == std::future_status::ready)
                {
                    std::cout << "[main get the promise result:   " << future.get() << std::endl;
                    break;  /*一定要退出,只能get一次,多次就会有异常*/
                }
            }
        }
        catch (const std::exception& e)
        {
            std::cerr << "error: " << e.what() << std::endl;
        }
        
                
        std::cout << "[main]  func will exit, start delete promise_________  " << sp.use_count() << std::endl;
    }

    th_.join();
    return 0;
}

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
#include <mutex>
#include <atomic>

std::mutex mtx_;
std::atomic<bool>  stop_ = false;
int memory = 0;
std::shared_ptr<std::promise<int>>  sp_po = nullptr;

void test()
{
	while (!stop_)
	{
		std::unique_lock<std::mutex> lck(mtx_);
		if (sp_po != nullptr)
		{
			try {
				sp_po->set_value(memory);
                sp_po = nullptr;
			}
			catch (std::exception& ec)
			{
				std::cout << "error " << ec.what() << std::endl;
			}
		}			
		lck.unlock();
		
		std::cout << "thread set value " << memory << std::endl;
		memory++;		
		std::this_thread::sleep_for(std::chrono::milliseconds(500));
	}
}

int main()
{	
	std::thread th(test);

	while(1)
	{		
		std::unique_lock<std::mutex> lck(mtx_);		
		sp_po.reset(new std::promise<int>);
		lck.unlock();

		std::cout << "main get value: " << sp_po->get_future().get() << std::endl;
	}


	return 0;
}

<think>嗯,用户之前问过如何在std::async创建子线程时立即获取线程号,我介绍了用promise和future的方法。现在他问这两个是什么,所以需要详细解释一下这两个的概念和用途。 用户可能对C++并发编程不太熟悉,特别是异步操作部分。需要先明确std::promisestd::future的基本定义,然后说明它们如何协同工作。用户可能想知道它们解决了什么问题,或者为什么在之前的例子中需要用到它们。 可能需要用简单的例子来辅助说明,比如数据传递或线程间通信。还要注意用户可能对线程同步、数据共享等概念有疑问,需要解释promise和future如何帮助安全地传递数据,避免竞争条件。 另外,用户可能想知道两者的区别,比如promise是设置值的一方,future是获取值的一方。需要强调它们的一对一关系,以及一次性的特性,即只能设置一次值,获取一次结果。 可能还需要提到异常传递的情况,如果promise中设置了异常,future在get时会抛出,这也是处理异步错误的一种方式。用户可能在实际使用中遇到异常处理的问题,这部分要说明清楚。 最后,结合之前的例子,说明在获取线程ID的场景下,promise和future是如何传递线程ID的,这样用户能更好地理解它们的实际应用。可能还需要提醒一些注意事项,比如生命周期管理和移动语义,避免悬空引用或对象复制的问题。 总结来说,用户需要的是对promise和future的全面介绍,包括它们的作用、如何使用、以及在实际中的应用场景,同时结合之前的例子加深理解。</think>`std::promise` 和 `std::future` 是 C++11 标准中引入的并发编程工具,用于实现 **跨线程的数据传递** 和 **同步**。它们通常结合使用,允许一个线程生成数据(通过 `promise`),另一个线程异步获取数据(通过 `future`)。以下是它们的核心概念和用法: --- ### **1. `std::promise`** #### **作用** - **生产者端**:用于在子线程中存储一个值(或异常),并通知其他线程该值已就绪。 - **核心方法**: - `set_value()`:设置值(只能调用一次)。 - `set_exception()`:设置异常(如任务执行失败时的错误信息)。 - `get_future()`:返回与当前 `promise` 关联的 `future` 对象。 #### **特** - 一个 `promise` 只能和一个 `future` 关联。 - 通过 `set_value` 或 `set_exception` 通知 `future` 数据已就绪。 --- ### **2. `std::future`** #### **作用** - **消费者端**:用于从异步操作中获取结果,支持阻塞或非阻塞的方式等待数据。 - **核心方法**: - `get()`:阻塞等待,直到数据就绪后返回结果(只能调用一次)。 - `wait()`:阻塞等待直到数据就绪。 - `wait_for()` / `wait_until()`:限时等待数据就绪。 - `valid()`:检查 `future` 是否关联有效的结果。 #### **特** - `future` 只能通过 `promise` 或 `std::async` 创建。 - `get()` 会转移所有权,调用后 `future` 不再有效。 -- ### **3. 协作流程** ```cpp std::promise<T> promise; // 生产者创建 promise std::future<T> future = promise.get_future(); // 消费者获取 future // 子线程(生产者)设置值 std::thread([&promise] { promise.set_value(42); // 设置结果 }).detach(); // 主线程(消费者)等待结果 T result = future.get(); // 阻塞直到结果就绪 ``` -- ### **4. 核心应用场景** #### **(1) 跨线程传递数据** ```cpp std::promise<int> p; std::future<int> f = p.get_future(); std::thread([&p] { p.set_value(100); // 子线程设置值 }).detach(); int value = f.get(); // 主线程获取值(阻塞等待) ``` #### **(2) 异步任务结果获取** 结合 `std::async` 实现异步计算: ```cpp std::future<int> future = std::async([] { return 42; // 在子线程中计算 }); int result = future.get(); // 主线程获取结果 ``` #### **(3) 异常传递** 子线程的异常可以通过 `promise` 传递到主线程: ```cpp std::promise<void> p; std::future<void> f = p.get_future(); std::thread([&p] { try { // 子线程可能抛出异常 throw std::runtime_error("子线程错误"); } catch (...) { p.set_exception(std::current_exception()); // 传递异常 } }).detach(); try { f.get(); // 主线程捕获异常 } catch (const std::exception& e) { std::cout << "捕获异常: " << e.what() << std::endl; } ``` --- ### **5. 关键特性** #### **(1) 一次性所有权** - `promise` 和 `future` 是一对一的关系,一个 `promise` 只能设置一次值,一个 `future` 只能调用一次 `get()`。 #### **(2) 线程安全** - `promise` 和 `future` 的方法本身是线程安全的,但需注意共享数据的竞态条件。 #### **(3) 移动语义** - `promise` 和 `future` 不可复制,但可以通过 `std::move` 转移所有权: ```cpp std::promise<int> p1; std::promise<int> p2 = std::move(p1); // 转移所有权 ``` --- ### **6. 与 `std::async` 的关系** - `std::async` 返回的 `future` 会自动关联到异步任务的结果: ```cpp std::future<int> f = std::async([] { return 42; }); int result = f.get(); // 获取结果 ``` - 若需要手动控制结果的传递(如传递线程号),需显式使用 `promise`。 --- ### **7. 示例:线程间传递复杂数据** ```cpp #include <iostream> #include <future> #include <thread> void task(std::promise<std::string> p) { std::this_thread::sleep_for(std::chrono::seconds(1)); p.set_value("子线程完成任务"); // 传递字符串 } int main() { std::promise<std::string> p; std::future<std::string> f = p.get_future(); std::thread(task, std::move(p)).detach(); std::cout << "主线程等待结果..." << std::endl; std::string result = f.get(); // 阻塞等待 std::cout << "结果: " << result << std::endl; return 0; } ``` --- ### **总结** - **`std::promise`**:生产者端工具,负责设置值或异常。 - **`std::future`**:消费者端工具,负责异步获取结果。 - **协作机制**:通过共享状态实现线程间数据同步,避免手动使用锁或条件变量。 - **适用场景**:异步任务结果传递、跨线程通信、异常处理等。 它们为 C++ 提供了一种高效、安全的异步编程范式,是并发编程的重要基础组件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何处惹尘埃~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值