C++11 多线程 future/promise简介

本文介绍了C++标准库中的并发编程组件,包括std::future用于访问异步操作结果,std::promise用于设置共享状态的值,std::packaged_task用于包装可调用目标以供异步调用,以及std::async如何结合这些组件实现异步任务。

目录(?)[+]

1. < future >头文件简介

  • Classes 
    std::future 
    std::future_error 
    std::packaged_task 
    std::promise 
    std::shared_future
  • Functions 
    std::async 
    std::future_category

2. std::future

简单来说,std::future提供了一种访问异步操作结果的机制。

从字面意思看,它表示未来。通常一个异步操作我们是不能马上就获取操作结果的,只能在未来某个时候获取。我们可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异步操作的结果。future_status有三种状态: 
deferred:异步操作还没开始 
ready:异步操作已经完成 
timeout:异步操作超时

获取future结果有三种方式:get、wait、wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值,wait_for是超时等待返回结果。

例子:

    //查询future的状态
    std::future_status status;
    do {
        status = future.wait_for(std::chrono::seconds(1));
        if (status == std::future_status::deferred) {
            std::cout << "deferred\n";
        } else if (status == std::future_status::timeout) {
            std::cout << "timeout\n";
        } else if (status == std::future_status::ready) {
            std::cout << "ready!\n";
        }
    } while (status != std::future_status::ready);
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3. std::promise

Promise对象可保存T类型的值,该值可被future对象读取(可能在另一个线程中),这是promise提供同步的一种手段。在构造promise时,promise对象可以与共享状态关联起来,这个共享状态可以存储一个T类型或者一个由std::exception派生出的类的值,并可以通过get_future来获取与promise对象关联的对象,调用该函数之后,两个对象共享相同的共享状态(shared state)。 
Promise对象是异步provider,它可以在某一时刻设置共享状态的值。 
Future对象可以返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标识变为ready,然后才能获取共享状态的值。

例子:

#include <iostream>       // std::cout
#include <functional>     // std::ref
#include <thread>         // std::thread
#include <future>         // std::promise, std::future

void print_int(std::future<int>& fut) {
    int x = fut.get(); // 获取共享状态的值.
    std::cout << "value: " << x << '\n'; // 打印 value: 10.
}

int main ()
{
    std::promise<int> prom; // 生成一个 std::promise<int> 对象.
    std::future<int> fut = prom.get_future(); // 和 future 关联.
    std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t.
    prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步.
    t.join();
    return 0;
}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

std::promise 构造函数

构造函数  
default (1) promise();
with allocator (2) template promise (allocator_arg_t aa, const Alloc& alloc);
copy [deleted] (3) promise (const promise&) = delete;
move (4) promise (promise&& x) noexcept;
  • 1.默认构造函数,初始化一个空的共享状态。
  • 2.带自定义内存分配器的构造函数,与默认构造函数类似,但是使用自定义分配器来分配共享状态。
  • 3.拷贝构造函数,被禁用。
  • 4.移动构造函数。 
    另外,std::promise 的 operator= 没有拷贝语义,即 std::promise 普通的赋值操作被禁用,operator= 只有 move 语义,所以 std::promise 对象是禁止拷贝的。

std::promise 成员函数

std::promise::get_future:返回一个与promise共享状态相关联的future对象 
std::promise::set_value:设置共享状态的值,此后promise共享状态标识变为ready 
std::promise::set_exception:为promise设置异常,此后promise的共享状态标识变为ready 
std::promise::set_value_at_thread_exit:设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready(注意:该线程已设置promise的值,如果在线程结束之后有其他修改共享状态值的操作,会抛出future_error(promise_already_satisfied)异常) 
std::promise::swap:交换 promise 的共享状态

4. std::packaged_task

std::packaged_task包装了一个可调用的目标(如function, lambda expression, bind expression, or another function object),以便异步调用,它和promise在某种程度上有点像,promise保存了一个共享状态的值,而packaged_task保存的是一个函数。

    std::packaged_task<int()> task([](){ return 7; });
    std::thread t1(std::ref(task)); 
    std::future<int> f1 = task.get_future(); 
    auto r1 = f1.get();
   
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

5. 小结

Promise,Future 和 Callback常常作为并发编程中一组非阻塞的模型。其中 Future 表示一个可能还没有实际完成的异步任务的【结果】,针对这个结果可以添加 Callback 以便在任务执行成功或失败后做出对应的操作,而 Promise 交由任务执行者,任务执行者通过 Promise 可以标记任务完成或者失败。

6. std::async

std::async大概的工作过程:先将异步操作用std::packaged_task包装起来,然后将异步操作的结果放到std::promise中,这个过程就是创造未来的过程。外面再通过future.get/wait来获取这个未来的结果。

可以说,std::async帮我们将std::future、std::promise和std::packaged_task三者结合了起来。

std::async的原型:

async(std::launch::async | std::launch::deferred, f, args...)
   
  • 1
  • 1

第一个参数是线程的创建策略,默认的策略是立即创建线程:

std::launch::async:在调用async就开始创建线程。 
std::launch::deferred:延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait时才创建线程。 
第二个参数是线程函数,后面的参数是线程函数的参数。

简单的例子:

    std::future<int> f1 = std::async(std::launch::async, [](){ 
        return 8;  
    }); 

    cout<<f1.get()<<endl; //output: 8

    std::future<int> f2 = std::async(std::launch::async, [](){ 
        cout<<8<<endl;
    }); 

    f2.wait(); //output: 8
C++ 中,`std::future` 和 `std::promise` 是用于线程间通信的重要工具,它们属于 C++11 引入的并发特性的一部分。`std::promise` 用于设置一个值或异常,而 `std::future` 用于获取这个值或处理异常。 ### 基本用法 一个 `std::promise` 对象与其关联的 `std::future` 对象之间存在一对一的关系。`std::promise` 用于提供一个值,而 `std::future` 用于在将来某个时刻获取该值。如果值尚未准备好,`std::future` 的 `get()` 方法会阻塞,直到值可用。 #### 示例代码 下面是一个简单的使用 `std::promise` 和 `std::future` 的示例: ```cpp #include <iostream> #include <future> #include <thread> void setter(std::promise<int>&& p) { int result = 42; p.set_value(result); // 设置 promise 的值 } int main() { std::promise<int> p; std::future<int> f = p.get_future(); // 获取与 promise 关联的 future std::thread t(setter, std::move(p)); // 启动线程,并传递 promise std::cout << "等待结果..." << std::endl; int result = f.get(); // 阻塞,直到 promise 设置了值 std::cout << "结果为: " << result << std::endl; t.join(); // 等待线程结束 return 0; } ``` 在这个例子中,`setter` 函数通过 `std::promise` 设置了一个值 `42`,主线程通过 `std::future` 获取这个值。`f.get()` 会阻塞,直到 `p.set_value()` 被调用。 ### 异常处理 `std::promise` 也可以用来传递异常。如果 `std::promise` 被销毁而没有调用 `set_value()` 或 `set_exception()`,它会自动设置一个 `std::future_error` 异常。 #### 示例代码(传递异常) ```cpp #include <iostream> #include <future> #include <thread> #include <stdexcept> void faultyFunction(std::promise<void>&& p) { try { throw std::runtime_error("Something went wrong!"); } catch (...) { p.set_exception(std::current_exception()); // 捕获异常并传递给 promise } } int main() { std::promise<void> p; std::future<void> f = p.get_future(); std::thread t(faultyFunction, std::move(p)); try { f.get(); // 抛出异常 } catch (const std::exception& e) { std::cout << "捕获到异常: " << e.what() << std::endl; } t.join(); return 0; } ``` 在这个例子中,`faultyFunction` 抛出了一个异常,并通过 `std::promise` 传递给了主线程。主线程的 `f.get()` 会重新抛出这个异常。 ### 线程池中的使用场景 在多线程编程中,特别是在线程池中,`std::future` 和 `std::promise` 可以用于任务提交和结果返回。例如,线程池可以接收一个任务,并返回一个 `std::future`,以便调用者可以在任务完成后获取结果。 #### 示例代码(线程池中的使用) ```cpp #include <iostream> #include <future> #include <thread> #include <vector> #include <functional> #include <queue> #include <mutex> #include <condition_variable> class ThreadPool { public: ThreadPool(size_t numThreads) { for (size_t i = 0; i < numThreads; ++i) { workers.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); if (this->stop && this->tasks.empty()) return; task = std::move(tasks.front()); tasks.pop(); } task(); } }); } } template<typename T> auto enqueue(T task) -> std::future<decltype(task())> { auto wrapper = std::make_shared<std::packaged_task<decltype(task())()>>(std::move(task)); { std::unique_lock<std::mutex> lock(queue_mutex); tasks.emplace([wrapper]() { (*wrapper)(); }); } condition.notify_one(); return wrapper->get_future(); } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for (std::thread& worker : workers) worker.join(); } private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stop = false; }; int main() { ThreadPool pool(4); auto result = pool.enqueue([]() -> int { std::this_thread::sleep_for(std::chrono::seconds(1)); return 42; }); std::cout << "等待结果..." << std::endl; std::cout << "结果为: " << result.get() << std::endl; return 0; } ``` 在这个例子中,`ThreadPool` 类使用 `std::packaged_task` 和 `std::future` 来管理任务的执行和结果返回。调用者可以通过 `result.get()` 获取任务的结果。 ### 总结 `std::future` 和 `std::promise` 是 C++ 中用于线程间通信的重要工具,它们可以帮助开发者实现异步任务和结果返回。通过合理使用这些工具,可以提高程序的并发性和响应能力。[^2] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值