@@ -343,33 +343,36 @@ std::async(f, n); // OK! 可以通过编译,不过引用的并非是局部
343
343
std::async(f2, n); // Error! 无法通过编译
344
344
```
345
345
346
- 我们来展示使用 `std::move` ,也就移动传递参数 :
346
+ 我们来展示使用 `std::move` ,也就是移动传递参数并接受返回值 :
347
347
348
348
```cpp
349
- struct move_only {
349
+ struct move_only{
350
350
move_only() { std::puts("默认构造"); }
351
- move_only(const move_only&) = delete;
352
- move_only(move_only&&)noexcept {
353
- std::puts("移动构造");
351
+ move_only(move_only&&)noexcept { std::puts("移动构造"); }
352
+ move_only& operator=(move_only&&) noexcept {
353
+ std::puts("移动赋值");
354
+ return *this;
354
355
}
356
+ move_only(const move_only&) = delete;
355
357
};
356
358
357
- void task(move_only x){
359
+ move_only task(move_only x){
358
360
std::cout << "异步任务 ID: " << std::this_thread::get_id() << '\n';
361
+ return x;
359
362
}
360
363
361
364
int main(){
362
365
move_only x;
363
- std::future<void > future = std::async(task, std::move(x));
364
- std::this_thread::sleep_for(std::chrono::milliseconds (1));
366
+ std::future<move_only > future = std::async(task, std::move(x));
367
+ std::this_thread::sleep_for(std::chrono::seconds (1));
365
368
std::cout << "main\n";
366
- future.wait (); // 等待异步任务执行完毕
369
+ move_only result = future.get (); // 等待异步任务执行完毕
367
370
}
368
371
```
369
372
370
- > [ 运行] ( https://godbolt.org/z/fY9Md3nzz ) 测试。
373
+ > [ 运行] ( https://godbolt.org/z/5cfvdEWWh ) 测试。
371
374
372
- 如你所见,它** 支持只移动类型** ,我们将参数使用 ` std::move ` 传递。
375
+ 如你所见,它** 支持只移动类型** ,我们将参数使用 ` std::move ` 传递,接收参数的时候直接调用 ` get ` 函数即可 。
373
376
374
377
---
375
378
@@ -672,6 +675,57 @@ int main() {
672
675
来自 set_exception 的异常: promise already satisfied
673
676
```
674
677
678
+ ### future 的状态变化
679
+
680
+ 需要注意的是,** future 是一次性的** ,所以你需要注意移动。并且,调用 ` get ` 函数后,future 对象也会** 失去共享状态** 。
681
+
682
+ - ** 移动语义** :这一点很好理解并且常见,因为** 移动操作标志着所有权的转移** ,意味着 ` future ` 不再拥有共享状态(如之前所提到)。` get ` 和 ` wait ` 函数要求 ` future ` 对象拥有共享状态,否则会抛出异常。
683
+ - ** 共享状态失效** :调用 ` get ` 成员函数时,` future ` 对象必须拥有共享状态,但调用完成后,它就会** 失去共享状态** ,不能再次调用 ` get ` 。这是我们在本节需要特别讨论的内容。
684
+
685
+ ``` cpp
686
+ std::future<void >future = std::async([] {});
687
+ std::cout << std::boolalpha << future.valid() << ' \n ' ; // true
688
+ future.get();
689
+ std::cout << std::boolalpha << future.valid() << ' \n ' ; // false
690
+ try {
691
+ future.get(); // 抛出 future_errc::no_state 异常
692
+ }
693
+ catch (std::exception& e) {
694
+ std::cerr << e.what() << '\n';
695
+ }
696
+ ```
697
+
698
+ > [ 运行] ( https://godbolt.org/z/hvfMGnMbj ) 测试。
699
+
700
+ 这个问题在许多文档中没有明确说明,但通过阅读源码([ MSVC STL] ( https://github.com/microsoft/STL/blob/f54203f/stl/inc/future ) ),可以很清楚地理解:
701
+
702
+ ``` cpp
703
+ // std::future<void>
704
+ void get () {
705
+ // block until ready then return or throw the stored exception
706
+ future _Local{_STD move(*this)};
707
+ _Local._Get_value();
708
+ }
709
+ // std::future<T>
710
+ _Ty get () {
711
+ // block until ready then return the stored result or throw the stored exception
712
+ future _Local{_STD move(*this)};
713
+ return _STD move(_Local._Get_value());
714
+ }
715
+ // std::future<T&>
716
+ _Ty& get () {
717
+ // block until ready then return the stored result or throw the stored exception
718
+ future _Local{_STD move(*this)};
719
+ return *_Local._Get_value();
720
+ }
721
+ ```
722
+
723
+ 如上所示,我们展示了 ` std::future ` 的所有特化 ` get ` 成员函数的实现。注意到了吗?尽管我们可能不了解移动构造函数的具体实现,但根据通用的语义,可以看出 ` future _Local{_STD move(*this)}; ` 将当前对象的共享状态转移给了这个局部对象,而局部对象在函数结束时析构。这意味着当前对象失去共享状态,并且状态被完全销毁。
724
+
725
+ 另外一提,` std::future<T> ` 这个特化,它 ` return std::move ` 是为了支持只能移动的类型能够使用 ` get ` 返回值,参见前文的 ` move_only ` 类型。
726
+
727
+ 如果需要进行多次 ` get ` 调用,可以考虑使用下文提到的 ` std::shared_future ` 。
728
+
675
729
### 多个线程的等待
676
730
677
731
之前的例子中都在用 ` std::future ` ,不过 ` std::future ` 也有局限性。很多线程在等待的时候,只有一个线程能获取结果。当多个线程等待相同事件的结果时,就需要使用 ` std::shared_future ` 来替代 ` std::future ` 了。` std::future ` 与 ` std::shared_future ` 的区别就如同 ` std::unique_ptr ` 、` std::shared_ptr ` 一样。
0 commit comments