描述C++中如何使用std::promise和std::future进行异步编程。

讲座主题:C++中的异步编程神器——std::promise 和 std::future

开场白

各位程序员朋友们,大家好!今天咱们来聊聊C++中的一对“黄金搭档”——std::promisestd::future。如果你觉得多线程编程像一场复杂的舞蹈,那么这两个家伙就是你的舞伴,帮你把节奏踩得稳稳的。

在现代C++中,异步编程已经成为了一种非常流行的编程模式。它允许我们在不阻塞主线程的情况下执行耗时任务,从而提升程序的性能和响应速度。而std::promisestd::future正是实现这一目标的重要工具。别担心,今天的讲座我会用轻松幽默的方式,带你一步步掌握它们的用法。


第一节:初识std::future

首先,我们来认识一下std::future。你可以把它想象成一个“未来”的容器,它会保存某个操作的结果。当你发起一个异步任务时,std::future就像是一个快递单号,你可以用它随时查询任务的状态或者获取最终的结果。

代码示例1:简单的std::future使用

#include <iostream>
#include <future>
#include <thread>

int main() {
    // 创建一个std::future对象
    std::future<int> futureResult = std::async(std::launch::async, []() {
        std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
        return 42;
    });

    std::cout << "Waiting for the result..." << std::endl;

    // 获取结果(阻塞直到结果可用)
    int result = futureResult.get();
    std::cout << "The result is: " << result << std::endl;

    return 0;
}

讲解:

  • std::async是C++标准库提供的一个函数,用于启动异步任务。
  • std::future在这里充当了结果的“快递单号”,我们可以用.get()方法获取最终的结果。
  • 注意:.get()是一个阻塞操作,如果结果还没准备好,它会一直等待。

第二节:std::promise登场

接下来,我们再看看std::promise。如果说std::future是一个“未来”的容器,那么std::promise就是一个“承诺”。它负责将结果传递给对应的std::future。换句话说,std::promise是生产者,std::future是消费者。

代码示例2:std::promise的基本用法

#include <iostream>
#include <future>
#include <thread>

void worker(std::promise<int> &&pr) {
    try {
        std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
        pr.set_value(42); // 设置结果
    } catch (...) {
        pr.set_exception(std::current_exception()); // 如果有异常,可以设置异常
    }
}

int main() {
    std::promise<int> promiseObj;
    std::future<int> futureObj = promiseObj.get_future(); // 获取关联的future

    std::thread t(worker, std::move(promiseObj)); // 将promise传递给worker线程

    std::cout << "Waiting for the worker to finish..." << std::endl;

    try {
        int result = futureObj.get(); // 获取结果
        std::cout << "Worker returned: " << result << std::endl;
    } catch (const std::exception &e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
    }

    t.join(); // 等待线程结束
    return 0;
}

讲解:

  • std::promise通过.set_value()方法将结果传递给关联的std::future
  • 如果任务中抛出了异常,可以用.set_exception()方法将异常传递给std::future
  • std::future可以通过.get()方法捕获异常并处理。

第三节:std::promise与std::future的协作

现在我们已经分别了解了std::promisestd::future,接下来让我们看看它们是如何协同工作的。

表格对比:std::promise vs std::future

特性 std::promise std::future
角色 生产者 消费者
主要用途 设置异步任务的结果 获取异步任务的结果
是否可以共享结果 不支持共享 支持通过std::shared_future共享
常见方法 .set_value(), .set_exception() .get(), .wait(), .valid()

代码示例3:std::promise与std::future的协作

#include <iostream>
#include <future>
#include <thread>

void producer(std::promise<int> &&pr) {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    pr.set_value(100); // 设置结果
}

void consumer(std::future<int> fu) {
    std::cout << "Consumer is waiting..." << std::endl;
    int result = fu.get(); // 获取结果
    std::cout << "Consumer received: " << result << std::endl;
}

int main() {
    std::promise<int> promiseObj;
    std::future<int> futureObj = promiseObj.get_future();

    std::thread producerThread(producer, std::move(promiseObj));
    std::thread consumerThread(consumer, std::move(futureObj));

    producerThread.join();
    consumerThread.join();

    return 0;
}

讲解:

  • 在这个例子中,std::promise负责生成结果,而std::future负责消费结果。
  • 通过分离生产和消费逻辑,我们可以更灵活地设计异步任务。

第四节:常见问题与注意事项

  1. std::future的阻塞问题
    .get()方法会阻塞当前线程,直到结果可用。如果不想阻塞,可以使用.wait_for().wait_until()来检查结果是否就绪。

  2. std::promise的移动语义
    std::promise对象不能被复制,只能通过移动语义传递。这是因为它的内部状态需要独占访问。

  3. 异常处理
    如果异步任务中抛出了异常,可以通过.set_exception()将异常传递给std::future,并在.get()时捕获。


结语

今天的讲座到这里就结束了!希望你对std::promisestd::future有了更深的理解。它们就像一对默契的搭档,帮助你在C++中轻松实现异步编程。记住,异步编程虽然强大,但也需要谨慎设计,避免出现死锁或数据竞争等问题。

最后,引用《C++ Concurrency in Action》这本书中的一句话:“并发编程不仅仅是让程序跑得更快,更是为了让程序更加健壮和可维护。”希望大家在学习异步编程的过程中,既能享受技术的乐趣,也能写出优雅的代码!

谢谢大家,下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注