首页 > 编程语言 >并发编程(8)—— std::async、std::future 源码解析

并发编程(8)—— std::async、std::future 源码解析

时间:2024-11-07 22:50:01浏览次数:5  
标签:std 调用 State state future async 源码

文章目录

八、day8

之前说过,std::async内部的处理逻辑和std::thread相似,而且std::asyncstd::future有密不可分的联系。今天,通过对std::asyncstd::future源码进行解析,了解二者的处理逻辑和关系。

源码均基于 MSVC 实现

参考:

  1. 博主恋恋风辰的个人博客
  2. up主mq白cpp的个人仓库

1. std::async

std::async有两种重载实现:

template <class _Fty, class... _ArgTypes>
_NODISCARD future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async(
    launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) {
    // manages a callable object launched with supplied policy
    using _Ret   = _Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>;
    using _Ptype = typename _P_arg_type<_Ret>::type;
    _Promise<_Ptype> _Pr(
        _Get_associated_state<_Ret>(_Policy, _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>(
                                                 _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...)));

    return future<_Ret>(_Pr._Get_state_for_future(), _Nil());
}

template <class _Fty, class... _ArgTypes>
_NODISCARD future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async(_Fty&& _Fnarg, _ArgTypes&&... _Args) {
    // manages a callable object launched with default policy
    return _STD async(launch::async | launch::deferred, _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...);
}

第一种重载需要显式指定启动策略,也就是我们之前说的std::launch::asyncstd::launch::deferredstd::launch::async | std::launch::deferred;第二种重载在使用默认策略时会被调用(也就是只传递可调用对象和参数而不传递启动策略),在内部会调用第一种重载并传入一个std::launch::async | std::launch::deferred策略,并将参数全部转发。

我们只需要着重关注第一种重载即可:

  1. 模板参数和函数体外部信息:

    • _Fty:可调用对象的类型
    • _ArgTypes:可调用对象所需的参数类型
    • _NODISCARD:宏,用于标记该函数的返回值不应被忽略
  2. 返回类型:

    future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>>
    

    其实就是返回一个 std::future 对象,_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>std::invoke对给定的可调用对象 _Fnarg 和参数 _Args... 执行后返回的类型,其实也就是通过 _Invoke_result_t_Fty_ArgTypes... 中推导出的返回类型。

    我们之前在thread源码解析中说过std::invoke内部其实是调用_Call函数,_Call函数负责提供参数并调用传入的可调用对象。

    我们可以把 _Invoke_result_t 看作是一个对 std::invoke 的结果类型的封装,std::invoke 是一个工具,可以调用可调用对象并返回其结果。_Invoke_result_t 提供了一种方式来“推导”出这个结果类型。

    这个类型萃取工具通常长这样(简化版):

    template <typename _Callable, typename... _Args>
    struct _Invoke_result_t {
        using type = decltype(std::invoke(std::declval<_Callable>(), std::declval<_Args>()...));
    };
    

    上述代码通过 std::invoke 来推导(decltype_Callable(即可调用对象)在给定参数 _Args... 上执行后的返回类型。换句话说,_Invoke_result_t<_Fty, _ArgTypes...>type 成员类型就是可调用对象在调用后的返回类型。

    值得注意的是,所有类型在传递前都进行了 decay 处理,也就是将cv和const修饰符去掉,默认按值传递与 std::thread 的行为一致。

  3. 形参:

    future<_Ret> async(launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) {}
    
    • launch _Policy: 表示任务的执行策略,可以是 launch::async(表示异步执行)或 launch::deferred(表示延迟执行),或者std::launch::async | std::launch::deferred
    • _Fty&& _Fnarg: 可调用对象,通过完美转发机制将其转发给实际的异步任务
    • _ArgTypes&&... _Args: 调用该可调用对象时所需的参数,同样通过完美转发机制进行转发
  4. _Ret_Ptype

    • _Ret就算我们在返回类型中说到的_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>,表示可调用对象的返回类型;

    • using _Ptype = typename _P_arg_type<_Ret>::type_Ptype 的定义在大多数情况下和 _Ret 是相同的,类模板 _P_arg_type 只是为了处理引用类型以及 void 的情况,参见 _P_arg_type 的实现:

      template <class _Fret>
      struct _P_arg_type { // type for functions returning T
          using type = _Fret;
      };
      
      template <class _Fret>
      struct _P_arg_type<_Fret&> { // type for functions returning reference to T
          using type = _Fret*;
      };
      
      template <>
      struct _P_arg_type<void> { // type for functions returning void
          using type = int;
      };
      

      为什么需要 _Ptype

      在异步任务的实现中,std::promise 是用于将结果与 std::future 绑定的对象。std::promise 的模板参数通常是可调用对象返回值的类型。在 std::async 函数中,我们需要创建一个 std::promise 对象来存储任务的结果,因此我们需要计算出正确的承诺类型(promise type)。也就是说,定义 _Ptype 是为了配合后面 _Promise 的使用,确保任务的结果可以通过 std::future 获取。

      • _Ret 是任务返回的类型(由 _Invoke_result_t 推导出)。

      • _Ptype 就是这个返回类型的承诺类型。也就是说,**_Ptypestd::promise 的模板参数类型,**表示这个任务结果的类型。

      _Ptype 的定义在大多数情况下和 _Ret 是相同的,都是可调用对象返回值的类型。

  5. _Promise<_Ptype> _Pr:创建一个 std::promise 对象 _Pr,其类型为 _Ptype,表示与异步任务的结果相关联的承诺(promise)。_Promise类型我们之前讲过,这里就不在叙述它的作用,关键还在于其存储的数据成员:

    template <class _Ty>
    class _Promise {
    public:
        _Promise(_Associated_state<_Ty>* _State_ptr) : _State(_State_ptr, false), _Future_retrieved(false) {}
    
        _Promise(_Promise&& _Other) : _State(_STD move(_Other._State)), _Future_retrieved(_Other._Future_retrieved) {}
    
        _Promise& operator=(_Promise&& _Other) {
            _State            = _STD move(_Other._State);
            _Future_retrieved = _Other._Future_retrieved;
            return *this;
        }
    
        ~_Promise() noexcept {}
    
        void _Swap(_Promise& _Other) {
            _State._Swap(_Other._State);
            _STD swap(_Future_retrieved, _Other._Future_retrieved);
        }
    
        const _State_manager<_Ty>& _Get_state() const {
            return _State;
        }
        _State_manager<_Ty>& _Get_state() {
            return _State;
        }
    
        _State_manager<_Ty>& _Get_state_for_set() {
            if (!_State.valid()) {
                _Throw_future_error(make_error_code(future_errc::no_state));
            }
    
            return _State;
        }
    
        _State_manager<_Ty>& _Get_state_for_future() {
            if (!_State.valid()) {
                _Throw_future_error(make_error_code(future_errc::no_state));
            }
    
            if (_Future_retrieved) {
                _Throw_future_error(make_error_code(future_errc::future_already_retrieved));
            }
    
            _Future_retrieved = true;
            return _State;
        }
    
        bool _Is_valid() const noexcept {
            return _State.valid();
        }
    
        bool _Is_ready() const {
            return _State._Is_ready();
        }
    
        bool _Is_ready_at_thread_exit() const {
            return _State._Is_ready_at_thread_exit();
        }
    
        _Promise(const _Promise&) = delete;
        _Promise& operator=(const _Promise&) = delete;
    
    private:
        _State_manager<_Ty> _State;
        bool _Future_retrieved;
    };
    

    注意:_Promisestd::promise 并不是同一个模板类,_Promise是为了提供对 std::promise 的进一步定制,并不是std::primse本身。std::primise模板类的私有成员是通过_Promise声明的,即

    // std::primise 的私有成员
    private:
        _Promise<_Ty*> _MyPromise;
    

    _Promise 类模板是对_State_manager类模板的包装,并增加了一个表示状态的私有成员 _Future_retrieved

    private:
        _State_manager<_Ty> _State;
        bool _Future_retrieved;
    

    状态成员用于跟踪 _Promise 是否已经调用过 _Get_state_for_future() 成员函数;它默认为 false,在第一次调用 _Get_state_for_future() 成员函数时被置为 true,如果二次调用,就会抛出future_errc::future_already_retrieved异常。

    _Promise 的构造函数接受的不是_State_manager 类型的对象,而是_Associated_state 类型的指针,用来初始化数据成员 _State

    _Promise(_Associated_state<_Ty>* _State_ptr) : _State(_State_ptr, false), _Future_retrieved(false) {}
    

    这是因为实际上_State_manager类型只有两个私有成员:Associated_state指针,以及一个状态成员:

    private:
        _Associated_state<_Ty>* _Assoc_state;
        bool _Get_only_once;
    

    可以简单理解为 _State_manager 是对 Associated_state 的包装,其中的大部分接口实际上是调用 _Assoc_state 的成员函数(你们可以去_State_manager的实现源码中查阅,大部分接口其实都是通过调用_Assoc_state实现的)。

    所以在解析std::async源码之前,我们必须对Associated_state有一个清晰的了解:

    public:
        _Ty _Result;
        exception_ptr _Exception;
        mutex _Mtx;
        condition_variable _Cond;
        bool _Retrieved;
        int _Ready;
        bool _Ready_at_thread_exit;
        bool _Has_stored_result;
        bool _Running;
    

    这是Associated_state模板类主要的成员变量(我没有全部列上去,只列了主要的),其中,最为重要的三个变量是:异常指针互斥量条件变量

    其实,_Associated_state 模板类负责管理异步任务的状态,包括结果的存储、异常的处理以及任务完成的通知。它是实现 std::futurestd::promise 的核心组件之一,通过 _State_manager_Promise 类模板对其进行封装和管理,提供更高级别的接口和功能。

    在这里插入图片描述

    _Promise、_State_manager_Associated_state 之间的包含关系如上述结构所示。

  6. 初始化 _Promise 对象:

    _Promise<_Ptype> _Pr(
        _Get_associated_state<_Ret>(_Policy, 
            _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>(
                _STD forward<_Fty>(_Fnarg), 
                _STD forward<_ArgTypes>(_Args)...
            )
        )
    );
    

    这是一个函数调用,将我们 std::async 的参数全部转发给它。

    1. 首先将参数 _Fnarg (可调用对象)和 _Args...(传入可调用对象的参数包) 通过 std::forward 转发给 _Fake_no_copy_callable_adapter
    2. 然后,_Fake_no_copy_callable_adapter 创建一个可调用对象(函数适配器)。
    3. 接着,适配器和指定的启动策略被传递给 _Get_associated_state 函数,目的是获取与异步操作相关的状态。
    4. 最终,_Get_associated_state 返回一个与异步操作相关的状态,并将其传递给 _Pr,这将会返回一个 _Promise<_Ptype>,代表一个异步操作的结果。

    _Get_associated_state函数根据启动模式(_Policy,有三种)来决定创建的异步任务状态对象类型:

    template <class _Ret, class _Fty>
    _Associated_state<typename _P_arg_type<_Ret>::type>* _Get_associated_state(launch _Psync, _Fty&& _Fnarg) {
        // construct associated asynchronous state object for the launch type
        switch (_Psync) { // select launch type
        case launch::deferred:
            return new _Deferred_async_state<_Ret>(_STD forward<_Fty>(_Fnarg));
        case launch::async: // TRANSITION, fixed in vMajorNext, should create a new thread here
        default:
            return new _Task_async_state<_Ret>(_STD forward<_Fty>(_Fnarg));
        }
    }
    

    _Get_associated_state 函数返回一个 _Associated_state 指针( _Associated_state 可用于初始化_State_manager,_State_manager可用于初始化_Promise),该指针指向一个新的 _Deferred_async_state_Task_async_state 对象。这两个类分别对应于异步任务的两种不同执行策略:延迟执行异步执行

    这段代码也很好的说明,launch::async | launch::deferredlaunch::async 的行为是相同的,都会创建新线程异步执行任务,只不过前者会自行判断系统资源来抉择。


    _Task_async_state_Deferred_async_state 都继承自 _Packaged_state,其用于异步执行任务。它们的构造函数都接受一个函数对象,并将其转发给基类 _Packaged_state 的构造函数。

    // _Task_async_state 的构造函数
    template <class _Rx>
    class _Task_async_state : public _Packaged_state<_Rx()>
    // _Deferred_async_state 的构造函数
    template <class _Rx>
    class _Deferred_async_state : public _Packaged_state<_Rx()>
    

    _Packaged_state 类型只有一个数据成员 :std::function 类型的对象 _Fn,它用来存储需要执行的异步任务,而它又继承自 _Associated_state

    template <class _Ret, class... _ArgTypes>
    class _Packaged_state<_Ret(_ArgTypes...)>
        : public _Associated_state<_Ret>
    

    在这里插入图片描述

    如上图所示,_Task_async_state_Deferred_async_state 都继承自 _Packaged_state_Packaged_state中保存了传入给std::async的可调用对象。同时,_Packaged_state继承自_Associated_state_Associated_state_Primise类中成员_State的最基本组成对象,基本所有的接口都是通过调用_Associated_state 的函数实现的。

    _Task_async_state_Deferred_async_state 的构造函数如下:

    // _Task_async_state
    template <class _Fty2>
    _Task_async_state(_Fty2&& _Fnarg) : _Mybase(_STD forward<_Fty2>(_Fnarg)) {
        _Task = ::Concurrency::create_task([this]() { // do it now
            this->_Call_immediate();
        });
    
        this->_Running = true;
    }
    // _Deferred_async_state
    template <class _Fty2>
    _Deferred_async_state(const _Fty2& _Fnarg) : _Packaged_state<_Rx()>(_Fnarg) {}
    template <class _Fty2>
    _Deferred_async_state(_Fty2&& _Fnarg) : _Packaged_state<_Rx()>(_STD forward<_Fty2>(_Fnarg)) {}
    

    a. _Task_async_state

    _Task_async_state有一个数据成员_Task用于从线程池中获取线程,并执行可调用对象:

    private:
        ::Concurrency::task<void> _Task;
    

    _Task_async_state 的实现使用了微软实现的并行模式库(PPL)。简而言之, launch::async 策略并不是单纯的创建线程让任务执行,而是使用了微软的 ::Concurrency::create_task ,它从线程池中获取线程并执行任务返回包装对象

    this->_Call_immediate()是调用 _Task_async_state 的父类 _Packaged_state 的成员函数 _Call_immediate.

    _Packaged_state有三个版本,自然_Call_immediate也有三种版本,用于处理可调用对象返回类型的三种情况

    // 返回普通类型
    // class _Packaged_state<void(_ArgTypes...)>
    void _Call_immediate(_ArgTypes... _Args) {
        _TRY_BEGIN
        // 调用函数对象并捕获异常 传递返回值
        this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), false);
        _CATCH_ALL
        // 函数对象抛出异常就记录
        this->_Set_exception(_STD current_exception(), false);
        _CATCH_END
    }
    
    // 返回引用类型
    // class _Packaged_state<_Ret&(_ArgTypes...)>
    void _Call_immediate(_ArgTypes... _Args) {
        _TRY_BEGIN
        // 调用函数对象并捕获异常 传递返回值的地址
        this->_Set_value(_STD addressof(_Fn(_STD forward<_ArgTypes>(_Args)...)), false);
        _CATCH_ALL
        // 函数对象抛出异常就记录
        this->_Set_exception(_STD current_exception(), false);
        _CATCH_END
    }
    
    // 返回void类型
    // class _Packaged_state<void(_ArgTypes...)>
    void _Call_immediate(_ArgTypes... _Args) { 
        _TRY_BEGIN
        // 调用函数对象并捕获异常 因为返回 void 不获取返回值 而是直接 _Set_value 传递一个 1
        _Fn(_STD forward<_ArgTypes>(_Args)...);
        this->_Set_value(1, false);
        _CATCH_ALL
        // 函数对象抛出异常就记录
        this->_Set_exception(_STD current_exception(), false);
        _CATCH_END
    }
    

    _Fn(_STD forward<_ArgTypes>(_Args)...表示执行可调用对象,this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...)表示将可调用对象的返回值传入给_Set_value,其他两个函数也是类似的处理过程。

    _TRY_BEGIN_CATCH_ALL_CATCH_END类似try-catch块。当 _Fn 函数对象抛出异常时,控制流会跳转到 _CATCH_ALL 代码块;this->_Set_exception用来记录当前捕获的异常;_CATCH_END 标识异常处理的结束;因为返回类型为void 表示不获取返回值,所以这里通过_Set_value 传递一个 1(表示正确执行的状态)。所有的返回值均传入给_Set_value

    简而言之,就是把返回引用类型的可调用对象返回值的引用获取地址传递给 _Set_value,把返回 void 类型的可调用对象传递一个 1 (表示正确执行的状态)给 _Set_value

    _Set_value_set_exception函数来自_Packaged_state模板类的父类_Associated_state,通过这两个函数,传递的可调用对象执行结果,以及可能的异常,并将结果或异常存储在 _Associated_state

    b. _Deferred_async_state

    _Deferred_async_state不会从线程池中获取一个新线程,然后再新线程中执行任务,而是当前线程调用future的get或者wait函数时,在当前线程同步执行。但它同样调用 _Call_immediate 函数执行存储的可调用对象,它有一个 _Run_deferred_function 函数:

    void _Run_deferred_function(unique_lock<mutex>& _Lock) override { // run the deferred function
        _Lock.unlock();
        _Packaged_state<_Rx()>::_Call_immediate();
        _Lock.lock();
    }
    

    然后通过 _Call_immediate调用可调用对象并通过函数_Set_value_set_exception存储可调用对象返回结果或者异常至_Associated_state

  7. 返回 std::future

    return future<_Ret>(_Pr._Get_state_for_future(), _Nil());
    

    _Ret在前面说了,其实就是可调用对象返回值的类型。

    传给future构造函数的参数之一是:_Pr._Get_state_for_future(),调用上面构造的_Promise的成员函数_Get_state_for_future,该函数用于返回_Promise类的私有成员变量_State

    _Get_state_for_future函数的实现如下:

    _State_manager<_Ty>& _Get_state_for_future() {
        if (!_State.valid()) {
            _Throw_future_error2(future_errc::no_state);
        }
    
        if (_Future_retrieved) {
            _Throw_future_error2(future_errc::future_already_retrieved);
        }
    
        _Future_retrieved = true;
        return _State;
    }
    

    其实就是调用_State的成员函数valid()检查状态(是否有错),然后判断future是否提前返回可调用对象的返回值(如果是,代表future的get被调用,抛出异常);最后,返回_State

2. std::future

我们首先从一个最简单的std::async示例开始:

std::future<int> future = std::async([] { return 0; });
future.get();

我们从之前的学习中了解到,future.get()就是从future中获取可调用对象的返回结果。唯一的问题是:future.get() 内部执行了什么流程?首先从future的实现开始:

_EXPORT_STD template <class _Ty>
class future : public _State_manager<_Ty> {
    // class that defines a non-copyable asynchronous return object that holds a value
private:
    using _Mybase = _State_manager<_Ty>;

public:
    static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>,
        "T in future<T> must meet the Cpp17Destructible requirements (N4950 [futures.unique.future]/4).");

    future() = default;

    future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {}

    future& operator=(future&&) = default;

    future(_From_raw_state_tag, const _Mybase& _State) noexcept : _Mybase(_State, true) {}

    _Ty get() {
        // block until ready then return the stored result or throw the stored exception
        future _Local{_STD move(*this)};
        return _STD move(_Local._Get_value());
    }

    _NODISCARD shared_future<_Ty> share() noexcept {
        return shared_future<_Ty>(_STD move(*this));
    }

    future(const future&)            = delete;
    future& operator=(const future&) = delete;
};

future类继承自_State_manager类,_State_manager类又有一个_Associated_state<_Ty>*类型的私有成员_State,而_State_manager的接口实现大部分是通过调用_Associated_state 的成员函数实现的。关系如下:

在这里插入图片描述

2.1 wait()

但你可能发现一个问题,future类怎么没有wait()成员函数????其实,wait()函数继承自父类_State_manager

void wait() const { // wait for signal
    if (!valid()) {
        _Throw_future_error2(future_errc::no_state);
    }

    _Assoc_state->_Wait();
}

_State_manager类的wait()其实是通过调用_Associated_state的接口实现的,所以说,_Associated_statestd::asyncstd::future中是非常核心的。

virtual void _Wait() { // wait for signal
    unique_lock<mutex> _Lock(_Mtx);
    _Maybe_run_deferred_function(_Lock);
    while (!_Ready) {
        _Cond.wait(_Lock);
    }
}

_Associated_statewait()函数通过unique_lock保护共享数据,然后调用_Maybe_run_deferred_function执行可调用对象,直至调用结束。

void _Maybe_run_deferred_function(unique_lock<mutex>& _Lock) { // run a deferred function if not already done
    if (!_Running) { // run the function
        _Running = true;
        _Run_deferred_function(_Lock);
    }
}

_Maybe_run_deferred_function其实就是通过调用_Run_deferred_function来调用_Call_immediate(),我们在async源码中学习过_Run_deferred_function和`_Call_immediate()。

void _Run_deferred_function(unique_lock<mutex>& _Lock) override { // run the deferred function
    _Lock.unlock();
    _Packaged_state<_Rx()>::_Call_immediate();
    _Lock.lock();
}

_Wait 函数中调用 _Maybe_run_deferred_function 是为了确保延迟执行(launch::deferred)的任务能够在等待前被启动并执行完毕。这样,在调用 wait 时可以正确地等待任务完成。

因为只有std::launch::deferred才是当调用future.get或者wait时才会执行_Call_immediate(),其他两种启动策略在大部分情况下都是直接执行,通过future.get获得结果。所以我们必须保证在调用wait函数时,执行std::launch::deferred策略的任务被执行,而其他两种启动策略早已经执行任务,无需再调用_Call_immediate()。所以在_Maybe_run_deferred_function函数中,有下面一段,判断任务是否以及执行,如果被执行,那么久就不调用_Call_immediate,反之调用。

    if (!_Running) { // run the function
        _Running = true;
        _Run_deferred_function(_Lock);

   while (!_Ready) {
        _Cond.wait(_Lock);
    }

通过条件变量挂起当前线程,等待可调用对象执行完毕。在等待期间,当前线程释放持有的锁,保证其他线程再次期间可以访问到共享资源,待当前线程被唤醒后,重新持有锁。其主要作用是:

  1. 避免虚假唤醒:
    • 条件变量的 wait 函数在被唤醒后,会重新检查条件(即 _Ready 是否为 true),确保只有在条件满足时才会继续执行。这防止了由于虚假唤醒导致的错误行为。
  2. 等待 launch::async 的任务在其它线程执行完毕:
    • 对于 launch::async 模式的任务,这段代码确保当前线程会等待任务在另一个线程中执行完毕,并接收到任务完成的信号。只有当任务完成并设置 _Readytrue 后,条件变量才会被通知,从而结束等待。

这样,当调用 wait 函数时,可以保证无论任务是 launch::deferred 还是 launch::async 模式,当前线程都会正确地等待任务的完成信号,然后继续执行。


std::future 其实还有两种特化,不过整体大差不差。

template <class _Ty>
class future<_Ty&> : public _State_manager<_Ty*>
template <>
class future<void> : public _State_manager<int>

也就是对返回类型为引用和 void 的情况了。其实先前已经聊过很多次了,无非就是内部的返回引用实际按指针操作,返回 void,那么也得给个 1,表示正常运行的状态。类似于前面 _Call_immediate 的实现。

2.2 get()

get()函数是future的成员函数,而没有继承父类_State_manager

// std::future<void>
void get() {
    // block until ready then return or throw the stored exception
    future _Local{_STD move(*this)};
    _Local._Get_value();
}
// std::future<T>
_Ty get() {
    // block until ready then return the stored result or throw the stored exception
    future _Local{_STD move(*this)};
    return _STD move(_Local._Get_value());
}
// std::future<T&>
_Ty& get() {
    // block until ready then return the stored result or throw the stored exception
    future _Local{_STD move(*this)};
    return *_Local._Get_value();
}

因为future有三种特化,所以get()函数也有三种特化。它们将当前future对象的指针通过std::move转移给类型为future的局部变量_Local(转移后,原本的future对象便失去了所有权)。然后,局部变量_Local调用成员函数_Get_value(),并将结果返回。

注意:局部对象 _Local 在函数结束时析构。这意味着当前对象(*this)失去共享状态,并且状态被完全销毁。

_Get_value() 函数的实现如下:

_Ty& _Get_value() const {
    if (!valid()) {
        _Throw_future_error2(future_errc::no_state);
    }

    return _Assoc_state->_Get_value(_Get_only_once);
}

future.valid() 成员函数检查 future 当前是否关联共享状态,即是否当前关联任务。如果还未关联,或者任务已经执行完(调用了 get()、set()),都会返回 false

  • 首先,通过valid()判断当前future对象是否关联共享状态,如果没,抛出异常。
  • 最后,调用 _Assoc_state 的成员函数 _Get_value ,传递 _Get_only_once 参数,其实就是代表这个成员函数只能调用一次。

_Assoc_state 的类型是 _Associated_state<_Ty>* ,是一个指针类型,它实际会指向自己的子类对象,我们在讲 std::async 源码的时候提到了,它必然指向 _Deferred_async_state 或者 _Task_async_state

_Assoc_state->_Get_value 这其实是个多态调用,父类有这个虚函数:

virtual _Ty& _Get_value(bool _Get_only_once) {
    unique_lock<mutex> _Lock(_Mtx);
    if (_Get_only_once && _Retrieved) {
        _Throw_future_error2(future_errc::future_already_retrieved);
    }

    if (_Exception) {
        _STD rethrow_exception(_Exception);
    }

    // TRANSITION: `_Retrieved` should be assigned before `_Exception` is thrown so that a `future::get`
    // that throws a stored exception invalidates the future (N4950 [futures.unique.future]/17)
    _Retrieved = true;
    _Maybe_run_deferred_function(_Lock);
    while (!_Ready) {
        _Cond.wait(_Lock);
    }

    if (_Exception) {
        _STD rethrow_exception(_Exception);
    }

    if constexpr (is_default_constructible_v<_Ty>) {
        return _Result;
    } else {
        return _Result._Held_value;
    }
}

子类 _Task_async_state 对其进行了重写,以 launch::async 策略或者std::launch::async | std::launch::deferred策略创建的future,实际会调用 _Task_async_state::_Get_value

_State_type& _Get_value(bool _Get_only_once) override {
    // return the stored result or throw stored exception
    _Task.wait();
    return _Mybase::_Get_value(_Get_only_once);
}

_Deferred_async_state 没有对其进行重写,直接调用父类虚函数。

_Task 就是 ::Concurrency::task<void> _Task;,调用 wait() 成员函数确保任务执行完毕。

_Mybase::_Get_value(_Get_only_once) 其实又是回去调用父类的虚函数了。

_Get_value 方法详解

  1. _Get_value()只能调用一次

    如果 _Get_only_oncetrue_Retrievedtrue(表示结果已经被检索过),则抛出 future_already_retrieved 错误。

  2. 处理异常

    如果在获取结果过程中出现了异常,需要重新抛出该异常

  3. 设置 _Retrievedtrue

    在获取值之前,设置 _Retrievedtrue,表示结果已经被检索过。这样可以确保 future 对象不会被重复获取,避免多次调用 get 时引发错误。

  4. 执行延迟函数

    调用_Maybe_run_deferred_function来运行可能的延迟任务。在该函数内部,如果任务已经运行,那么退出,如果没运行,调用_Call_immediate()函数执行可调用对象。

  5. 等待结果

    使用条件变量挂起当前线程,确保线程同步,即只有当异步任务准备好返回结果时,线程才会继续执行。

  6. 再次检查异常

    线程被唤醒将结果存储至future对象中后,再次判断是否发生了异常,需要重新抛出异常

  7. 返回结果

    这部分代码根据 _Ty 类型的特性决定如何返回结果:

    • 如果 _Ty 类型是 默认可构造(即 _Ty 的默认构造函数有效),直接返回 _Result
    • 否则,返回 _Result._Held_value

    is_default_constructible_v<_Ty> 是一个 C++17 引入的类型特征,用于检查类型 _Ty 是否具有默认构造函数。

    _Resultfuture 中持有的结果,而 _Held_value 是存储在 _Result 中的实际值。

_Result 是通过执行 _Call_immediate 函数,然后 _Call_immediate 再执行 _Set_value_Set_value 再执行 _Set_value_raw_Set_value_raw再执行_Emplace_result并通知线程可以醒来,_Emplace_result 获取到我们执行任务的返回值的。以 Ty 的偏特化为例:

// _Packaged_state
void _Call_immediate(_ArgTypes... _Args) {
    _TRY_BEGIN
    // 调用函数对象并捕获异常 传递返回值
    this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), false);
    _CATCH_ALL
    // 函数对象抛出异常就记录
    this->_Set_exception(_STD current_exception(), false);
    _CATCH_END
}

// _Asscoiated_state
void _Set_value(const _Ty& _Val, bool _At_thread_exit) { // store a result
    unique_lock<mutex> _Lock(_Mtx);
    _Set_value_raw(_Val, &_Lock, _At_thread_exit);
}
void _Set_value_raw(const _Ty& _Val, unique_lock<mutex>* _Lock, bool _At_thread_exit) {
    // store a result while inside a locked block
    if (_Already_has_stored_result()) {
        _Throw_future_error2(future_errc::promise_already_satisfied);
    }

    _Emplace_result(_Val);
    _Do_notify(_Lock, _At_thread_exit);
}
template <class _Ty2>
void _Emplace_result(_Ty2&& _Val) {
    // TRANSITION, incorrectly assigns _Result when _Ty is default constructible
    if constexpr (is_default_constructible_v<_Ty>) {
        _Result = _STD forward<_Ty2>(_Val); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    } else {
        ::new (static_cast<void*>(_STD addressof(_Result._Held_value))) _Ty(_STD forward<_Ty2>(_Val));
        _Has_stored_result = true;
    }
}

标签:std,调用,State,state,future,async,源码
From: https://blog.csdn.net/m0_63086198/article/details/143609381

相关文章

  • php毕业设计流浪动物领养系统宠物领养网站PHP+MySQL+HTML计算机毕业设计PHP源码获取ph
    一、功能介绍基于php+mysql+html前台:网站首页宠物推荐最新宠物新闻咨询宠物分类宠物资讯评论登录/注册加入购物车、领养、个人中心后台:各种增删改查系统设置分类管理宠物管理领养管理评价管理新闻资讯用户管理二、效果展示三、代码展示CREATE......
  • SpringBoot校园二手书籍信息平台w5mzh--(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究目的与意义随着高校教育的普及,书籍成为大学生日常学习不可或缺的资源。然而,书籍的高消耗量和频繁更新导致了资源的浪费。因此,构建一个校园......
  • 免费送源码:python+Django+MySQL Django实验室管理系统 计算机毕业设计原创定制
    摘 要本论文主要论述了如何使用python语言、Django框架开发一个实验室管理系统,本系统将严格按照软件开发流程,进行各个阶段的工作,面向对象编程思想进行项目开发。在引言中,作者将论述该系统的当前背景以及系统开发的目的,后续章节将严格按照软件开发流程,对系统进行各个阶段分......
  • 毕业设计-精品在线试题库系统源码+10000字论文
    项目简介精品在线试题库系统是一个多功能的在线教育平台,旨在提高教学和学习的效率。系统主要服务于三个核心角色:管理员、教师和学生,每个角色都有特定的功能权限。管理员功能:个人中心:管理员的个性化操作界面。专业管理:添加、查询、修改和删除专业信息。学生管理:对学生信息......
  • 毕业设计-springboot班级回忆录系统源码+论文
    项目简介海滨学院班级回忆录系统是一个综合性的在线平台,旨在为校友提供一个回忆和分享大学时光的空间。系统主要服务于管理员、班委和普通用户,提供了以下核心功能:管理员功能:    个人中心:管理员的个性化操作界面。    用户信息管理:管理员可以查看和管理用户信......
  • [免费]SpringBoot+Vue(高校)学籍管理系统【论文+源码+SQL脚本】
    大家好,我是java1234_小锋老师,看到一个不错的SpringBoot+Vue(高校)学籍管理系统,分享下哈。项目视频演示【免费】SpringBoot+Vue(高校)学籍管理系统Java毕业设计_哔哩哔哩_bilibili项目介绍对在线学籍管理的流程进行科学整理、归纳和功能的精简,通过软件工程的研究方法,结合当......
  • 基于nodejs+vue在线音乐网站[开题+源码+程序+论文]计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于在线音乐网站的研究,现有研究多侧重于音乐推荐算法、版权管理等方面3。专门针对在线音乐网站整体功能架构,包括用户、歌手分类、歌曲信息等综合系统功......
  • 基于nodejs+vue在线音乐播放平台[开题+源码+程序+论文]计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于在线音乐播放平台的研究,现有研究主要集中在大型商业音乐平台的整体运营、用户体验优化等方面,如QQ音乐、网易云音乐等平台的功能拓展、营销策略等。......
  • 基于JAVA的在线购物平台设计与实现-计算机毕设 附源码 26720
    基于JAVA的在线购物平台设计与实现摘要基于JAVA的在线购物平台设计与实现是一个涉及到软件开发和电子商务的综合课题。在这样的平台上,用户可以浏览商品、将商品加入购物车、进行下单购买等操作。为了实现这一功能,需要考虑到前后端的交互、数据库的设计、安全性和用户体......
  • Springboot游戏网站322mj(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,游戏信息,游戏分类,拍卖行,卖家,装备购买,游戏周边,周边购买,点卡充值,充值信息,处罚公示,处罚申诉,商品分类开题报告内容一、研究背景与意义随着信息技术......