跳转至

5. Future

使用下述功能需要包含头文件coke/future.h

常量

下述int类型常量与coke::Future的状态有关,位于coke命名空间。

// 结果已经被设置
constexpr int FUTURE_STATE_READY        = 0;
// 等待结果超时
constexpr int FUTURE_STATE_TIMEOUT      = 1;
// 等待结果时进程退出
constexpr int FUTURE_STATE_ABORTED      = 2;
// 关联的coke::Promise已经析构,但未设置结果或异常
constexpr int FUTURE_STATE_BROKEN       = 3;
// 异常已经被设置
constexpr int FUTURE_STATE_EXCEPTION    = 4;
// coke::Promise尚未通知任何状态
constexpr int FUTURE_STATE_NOTSET       = 5;

coke::Future

coke::Future提供等待和获取异步操作结果的机制。每个coke::Promise实例与唯一一个coke::Future关联,其中coke::Promise用于设置结果或异常,coke::Future用于等待结果或异常。

template<Cokeable Res>
class Future;

成员函数

  • 构造函数/析构函数

    可默认构造、可移动构造,不可复制构造。默认构造的对象不与任何coke::Promise关联,通常应从构造好的coke::Promise对象获取coke::Future实例。

    Future();
    
    Future(Future &&);
    
    Future(const Future &) = delete;
    
    ~Future();
    
  • 检查状态是否有效

    返回当前Future的状态是否有效。默认构造的Future无效,通过Promise获取的Future有效。

    bool valid() const;
    
  • 获取Future的内部状态

    获取Future的当前状态,可参考常量coke::FUTURE_STATE_XXX

    int get_state() const;
    
  • 检查是否已经设置了结果

    前提是valid()返回true

    bool ready() const;
    
  • 检查是否未设置结果或异常但Promise被销毁

    前提是valid()返回true

    bool broken() const;
    
  • 检查是否设置了异常

    前提是valid()返回true

    bool has_exception() const;
    
  • 阻塞直到Future就绪

    协程返回值参考coke::FUTURE_STATE_XXX常量。

    coke::Task<int> wait();
    
  • 阻塞直到Future就绪或超时

    协程返回值参考coke::FUTURE_STATE_XXX常量。

    coke::Task<int> wait_for(const coke::NanoSec &nsec);
    
  • 获取由Promise设置的值

    前提是ready()返回true。如果有异常,将重新抛出。该函数最多只能调用一次。

    Res get();
    
  • 获取关联的异常

    前提是has_exception()返回true

    std::exception_ptr get_exception();
    
  • 设置回调函数

    设置一个回调函数,当Future状态不再是FUTURE_STATE_NOTSET时调用该函数,若已不是则立即调用。该函数可用于帮助实现异步等待多个Future完成,可参考下文的辅助函数部分。

    void set_callback(std::function<void(int)> callback);
    
  • 移除回调函数

    移除之前设置的回调函数。

    void remove_callback();
    
  • 发送取消信号

    向关联的Promise发送一个取消信号,但如何处理该信号取决于Promise

    void cancel();
    

coke::Promise

Promise用于设置异步操作的结果,与唯一一个Future关联。

template<Cokeable Res>
class Promise;

成员函数

  • 构造函数/析构函数

    可默认构造,可移动构造,不可复制构造。析构函数会在Promise未设置值的情况下标记为FUTURE_STATE_BROKEN状态。

    Promise();
    
    Promise(Promise &&) = default;
    
    Promise(const Promise &) = delete;
    
    ~Promise();
    
  • 获取与Promise关联的Future

    获取与该Promise关联的Future,该函数至多调用一次。

    coke::Future<Res> get_future();
    
  • 查看取消信号

    查看关联的Future是否设置了取消信号。

    bool is_canceled() const noexcept;
    
  • 设置值并唤醒Future

    设置值并唤醒与之关联的Future,每个Promise仅可修改一次内部状态,如果状态已经设置,将返回false

    template<typename U> requires std::is_same_v<Res, std::remove_cvref_t<U>>
    bool set_value(U &&value);
    
  • 设置值的特化版本(Resvoid

    设置值并唤醒与之关联的Future,每个Promise仅可修改一次内部状态,如果状态已经设置,将返回false

    bool set_value() requires std::is_same_v<Res, void>;
    
  • 设置异常并唤醒Future

    设置异常并唤醒与之关联的Future,每个Promise仅可修改一次内部状态,如果状态已经设置,将返回false

    bool set_exception(const std::exception_ptr &eptr);
    

示例

#include <iostream>
#include <chrono>
#include <string>

#include "coke/future.h"
#include "coke/sleep.h"
#include "coke/wait.h"

using namespace std::chrono_literals;

constexpr int SET_VALUE = 0;
constexpr int SET_EXCEPTION = 1;
constexpr int SET_NOTHING = 2;

coke::Task<> set_promise(coke::Promise<std::string> p, int s) {
    try {
        co_await coke::sleep(100ms);
        if (s == SET_EXCEPTION)
            throw 1;
    }
    catch(...) {
        p.set_exception(std::current_exception());
    }

    if (s == SET_VALUE)
        p.set_value(std::string{"hello"});
}

coke::Task<> wait_future(int s) {
    coke::Promise<std::string> p;
    coke::Future<std::string> f = p.get_future();

    // 以分离模式运行协程,保证会在未来某个时刻为p设置值
    coke::detach(set_promise(std::move(p), s));

    // 当前协程不会阻塞,可继续其他操作,或尝试等待结果
    while (true) {
        int ret = co_await f.wait_for(30ms);
        if (ret == coke::FUTURE_STATE_TIMEOUT) {
            std::cout << "wait timeout" << std::endl;
            continue;
        }

        if (ret == coke::FUTURE_STATE_READY) {
            std::cout << "wait success " << f.get() << std::endl;
        }
        else if (ret == coke::FUTURE_STATE_EXCEPTION) {
            std::cout << "wait exception" << std::endl;
        }
        else if (ret == coke::FUTURE_STATE_BROKEN) {
            std::cout << "wait broken" << std::endl;
        }
        else {
            std::cout << "wait unknown error " << ret << std::endl;
        }
        break;
    }
}

int main() {
    coke::sync_wait(wait_future(SET_VALUE));
    coke::sync_wait(wait_future(SET_EXCEPTION));
    coke::sync_wait(wait_future(SET_NOTHING));
    return 0;
}

辅助函数

  • 创建一个Future并立即启动任务

    coke::Task<T>创建一个coke::Future<T>,任务将在正在运行的任务流series上立即启动。

    template<Cokeable T>
    coke::Future<T> create_future(Task<T> &&task, SeriesWork *series);
    
  • 创建一个Future并立即启动任务(自定义任务流创建器)

    coke::Task<T>创建一个coke::Future<T>,任务将在新创建的任务流中立即启动。

    template<Cokeable T, typename SeriesCreaterType>
    coke::Future<T> create_future(coke::Task<T> &&task, SeriesCreaterType &&creater);
    
  • 创建一个Future并立即启动任务(默认创建器)

    coke::Task<T>创建一个coke::Future<T>,任务将在默认的任务流创建器中立即启动。

    template<Cokeable T>
    coke::Future<T> create_future(coke::Task<T> &&task);
    
  • 等待至少nFuture完成

    注意参数使用引用传递,调用者应保证该协程结束前参数不会被修改或析构。

    template<Cokeable T>
    coke::Task<void> wait_futures(std::vector<coke::Future<T>> &futs, std::size_t n);
    
  • 等待至少nFuture完成或超时

    注意参数使用引用传递,调用者应保证该协程结束前参数不会被修改或析构。协程返回int类型整数,若等待成功则为coke::TOP_SUCCESS,若超时则为coke::TOP_TIMEOUT

    template<Cokeable T>
    coke::Task<int> wait_futures_for(std::vector<coke::Future<T>> &futs, std::size_t n,
                                     coke::NanoSec nsec);