Skip to content

Commit 2fae072

Browse files
committed
更新部分 async 的源码解析 #12
1 parent bd41c96 commit 2fae072

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

Diff for: md/详细分析/03async与future源码解析.md

+104
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,106 @@
11
# `st::async``std::future` 源码解析
22

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

Comments
 (0)