|
1 | 1 | # `st::async` 与 `std::future` 源码解析
|
2 | 2 |
|
| 3 | +## 前言 |
| 4 | + |
| 5 | +和之前一样的,我们以 MSVC STL 的实现进行讲解。 |
| 6 | + |
| 7 | +`std::future`,即未来体,是用来管理一个共享状态的类模板,用于等待关联任务的完成并获取其返回值。它自身不包含状态,需要通过如 `std::async` 之类的函数进行初始化。`std::async` 函数模板返回一个已经初始化且具有共享状态的 `std::future` 对象。因此,所有操作的开始应从 `std::async` 开始讲述。 |
| 8 | + |
| 9 | +> MSVC STL 很早之前就不支持 C++11 了,它的实现完全基于 **C++14**,出于某些原因 **C++17** 的一些库(如 [`invoke`](https://zh.cppreference.com/w/cpp/utility/functional/invoke), _v 变量模板)被向后移植到了 **C++14** 模式,所以即使是 C++11 标准库设施,实现中可能也是使用到了 C++14、17 的东西。 |
| 10 | +> |
| 11 | +> 注意,不用感到奇怪。 |
| 12 | +
|
| 13 | +## 正式 |
| 14 | + |
| 15 | +```cpp |
| 16 | +_EXPORT_STD template <class _Fty, class... _ArgTypes> |
| 17 | +_NODISCARD_ASYNC future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async( |
| 18 | + launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) { |
| 19 | + // manages a callable object launched with supplied policy |
| 20 | + using _Ret = _Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>; |
| 21 | + using _Ptype = typename _P_arg_type<_Ret>::type; |
| 22 | + _Promise<_Ptype> _Pr( |
| 23 | + _Get_associated_state<_Ret>(_Policy, _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>( |
| 24 | + _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...))); |
| 25 | + |
| 26 | + return future<_Ret>(_From_raw_state_tag{}, _Pr._Get_state_for_future()); |
| 27 | +} |
| 28 | + |
| 29 | +_EXPORT_STD template <class _Fty, class... _ArgTypes> |
| 30 | +_NODISCARD_ASYNC future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async( |
| 31 | + _Fty&& _Fnarg, _ArgTypes&&... _Args) { |
| 32 | + // manages a callable object launched with default policy |
| 33 | + return _STD async(launch::async | launch::deferred, _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...); |
| 34 | +} |
| 35 | +``` |
| 36 | +
|
| 37 | +这段代码最直观的信息是,函数模板 `std::async` 有两个重载,其中第二个重载只是给了一个执行策略并将参数全部转发,调用第一个重载。也就是不指明执行策略的时候就会匹配到第二个重载版本。因此我们也只需要关注第二个版本了。 |
| 38 | +
|
| 39 | +1. **模板参数和函数体外部信息**: |
| 40 | +
|
| 41 | + - `_Fty` 表示可调用对象的类型。 |
| 42 | + - `_ArgTypes` 是一个类型形参包,表示调用该可调用对象所需的参数类型。 |
| 43 | + - `_NODISCARD_ASYNC` 是一个宏,表示属性 `[[nodiscard]]`,用于标记此函数的返回值不应被忽略。 |
| 44 | +
|
| 45 | +2. 函数返回类型: |
| 46 | +
|
| 47 | + ```cpp |
| 48 | + future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> |
| 49 | + ``` |
| 50 | + |
| 51 | + 虽然看起来复杂,但实际上是通过 `_Invoke_result_t` 获取可调用对象的返回类型。与标准库中的 [`std::invoke_result_t`](https://zh.cppreference.com/w/cpp/types/result_of) 基本相同。 |
| 52 | + |
| 53 | + 可以举一个使用 `std::invoke_result_t` 的例子: |
| 54 | + |
| 55 | + ```cpp |
| 56 | + template<class Fty, class... ArgTypes> |
| 57 | + std::future<std::invoke_result_t<std::decay_t<Fty>,std::decay_t<ArgTypes>...>> |
| 58 | + test_fun(Fty&& f,ArgTypes&&... args){ |
| 59 | + return std::async(std::launch::async, std::forward<Fty>(f), std::forward<ArgTypes>(args)...); |
| 60 | + } |
| 61 | + |
| 62 | + auto result = test_fun([](int) {return 1; }, 1); // std::future<int> |
| 63 | + ``` |
| 64 | +
|
| 65 | + 值得注意的是,所有类型在传递前都进行了 [`decay`](https://zh.cppreference.com/w/cpp/types/decay) 处理,也就是说不存在引用类型,是默认按值传递与 `std::thread` 的行为一致。 |
| 66 | +
|
| 67 | +3. 函数形参: |
| 68 | +
|
| 69 | + ```cpp |
| 70 | + async(launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) |
| 71 | + ``` |
| 72 | + |
| 73 | + `launch _Policy`: 表示任务的执行策略,可以是 `launch::async`(表示异步执行)或 `launch::deferred`(表示延迟执行),或者两者的组合。 |
| 74 | + |
| 75 | + `_Fty&& _Fnarg`: 可调用对象,通过完美转发机制将其转发给实际的异步任务。 |
| 76 | + |
| 77 | + `_ArgTypes&&... _Args`: 调用该可调用对象时所需的参数,同样通过完美转发机制进行转发。 |
| 78 | + |
| 79 | +4. `using _Ret = _Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>;` |
| 80 | + |
| 81 | + `using _Ptype = typename _P_arg_type<_Ret>::type;` |
| 82 | + |
| 83 | + - 定义 `_Ret` 类型别名,它是使用 `_ArgTypes` 类型参数调用 `_Fty` 类型的可调用对象后得到的结果类型。也就是我们传入的可调用对象的返回类型;同样使用了 `_Invoke_result_t` 与 `decay_t`。 |
| 84 | + |
| 85 | + - 其实 `_Ptype` 的定义确实在大多数情况下和 `_Ret` 是相同的,类模板 _P_arg_type 只是为了处理引用类型以及 void 的情况,参见 `_P_arg_type` 的实现: |
| 86 | + |
| 87 | + ```cpp |
| 88 | + template <class _Fret> |
| 89 | + struct _P_arg_type { // type for functions returning T |
| 90 | + using type = _Fret; |
| 91 | + }; |
| 92 | + |
| 93 | + template <class _Fret> |
| 94 | + struct _P_arg_type<_Fret&> { // type for functions returning reference to T |
| 95 | + using type = _Fret*; |
| 96 | + }; |
| 97 | + |
| 98 | + template <> |
| 99 | + struct _P_arg_type<void> { // type for functions returning void |
| 100 | + using type = int; |
| 101 | + }; |
| 102 | + ``` |
| 103 | + |
| 104 | + `_Ptype`:处理异步任务返回值的方式类型,它在语义上强调了异步任务返回值的处理方式,具有不同的实现逻辑和使用场景。在当前我们难以直接展示它的作用,将在后文详细探讨 `_Promise` 类型的内部实现,展示此别名的意义。 |
| 105 | + |
| 106 | +5. |
0 commit comments