@@ -273,6 +273,8 @@ _NODISCARD_ASYNC future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>>
273
273
274
274
`_Get_associated_state` 函数返回一个 `_Associated_state` 指针,该指针指向一个新的 `_Deferred_async_state` 或 `_Task_async_state` 对象。这两个类分别对应于异步任务的两种不同执行策略:**延迟执行**和**异步执行**。
275
275
276
+ > 其实就是父类指针指向了子类对象,注意 **`_Associated_state` 是有虚函数的**,子类进行覆盖,这很重要。比如在后续聊 `std::future` 的 `get()` 成员函数的时候就会讲到
277
+
276
278
> 这段代码也很好的说明在 MSVC STL 中,`launch::async | launch::deferred` 和 `launch::async` 的行为是相同的,即都是异步执行。
277
279
278
280
---
@@ -580,7 +582,7 @@ virtual void _Wait() { // wait for signal
580
582
}
581
583
```
582
584
583
- 先使用锁进行保护,然后调用函数,再循环等待任务执行完毕。关键实际上在于 ` _Maybe_run_deferred_function ` :
585
+ 先使用锁进行保护,然后调用函数,再循环等待任务执行完毕。` _Maybe_run_deferred_function ` :
584
586
585
587
``` cpp
586
588
void _Maybe_run_deferred_function (unique_lock<mutex >& _ Lock) { // run a deferred function if not already done
@@ -591,4 +593,202 @@ void _Maybe_run_deferred_function(unique_lock<mutex>& _Lock) { // run a deferred
591
593
}
592
594
```
593
595
594
- `_Run_deferred_function` 相信你不会陌生,在讲述 `std::async` 源码中其实已经提到了。
596
+ `_Run_deferred_function` 相信你不会陌生,在讲述 `std::async` 源码中其实已经提到了,就是解锁然后调用 `_Call_immediate` 罢了。
597
+
598
+ ```cpp
599
+ void _Run_deferred_function(unique_lock<mutex>& _Lock) override { // run the deferred function
600
+ _Lock.unlock();
601
+ _Packaged_state<_Rx()>::_Call_immediate();
602
+ _Lock.lock();
603
+ }
604
+ ```
605
+
606
+ > ` _Call_immediate ` 就是执行我们实际传入的函数对象,先前已经提过。
607
+
608
+ 在 ` _Wait ` 函数中调用 ` _Maybe_run_deferred_function ` 是为了确保延迟执行(` launch::deferred ` )的任务能够在等待前被启动并执行完毕。这样,在调用 ` wait ` 时可以正确地等待任务完成。
609
+
610
+ 至于下面的循环等待部分:
611
+
612
+ ``` cpp
613
+ while (!_Ready) {
614
+ _Cond.wait(_Lock);
615
+ }
616
+ ```
617
+
618
+ 这段代码使用了条件变量、互斥量、以及一个状态对象,主要目的有两个:
619
+
620
+ 1 . ** 避免虚假唤醒** :
621
+ - 条件变量的 ` wait ` 函数在被唤醒后,会重新检查条件(即 ` _Ready ` 是否为 ` true ` ),确保只有在条件满足时才会继续执行。这防止了由于虚假唤醒导致的错误行为。
622
+ 2 . ** 等待 ` launch::async ` 的任务在其它线程执行完毕** :
623
+ - 对于 ` launch::async ` 模式的任务,这段代码确保当前线程会等待任务在另一个线程中执行完毕,并接收到任务完成的信号。只有当任务完成并设置 ` _Ready ` 为 ` true ` 后,条件变量才会被通知,从而结束等待。
624
+
625
+ 这样,当调用 ` wait ` 函数时,可以保证无论任务是 ` launch::deferred ` 还是 ` launch::async ` 模式,当前线程都会正确地等待任务的完成信号,然后继续执行。
626
+
627
+ ---
628
+
629
+ ` wait() ` 介绍完了,那么接下来就是 ` get() ` :
630
+
631
+ ``` cpp
632
+ // std::future<void>
633
+ void get () {
634
+ // block until ready then return or throw the stored exception
635
+ future _Local{_STD move(*this)};
636
+ _Local._Get_value();
637
+ }
638
+ // std::future<T>
639
+ _Ty get () {
640
+ // block until ready then return the stored result or throw the stored exception
641
+ future _Local{_STD move(*this)};
642
+ return _STD move(_Local._Get_value());
643
+ }
644
+ // std::future<T&>
645
+ _Ty& get () {
646
+ // block until ready then return the stored result or throw the stored exception
647
+ future _Local{_STD move(*this)};
648
+ return *_Local._Get_value();
649
+ }
650
+ ```
651
+
652
+ 在第四章的 “* future 的状态变化* ”一节中我们也详细聊过 ` get() ` 成员函数。由于 future 本身有三个特化,` get() ` 成员函数自然那也有三个版本,不过总体并无多大区别。
653
+
654
+ 它们都是将当前对象(` *this ` )的共享状态转移给了这个局部对象 ` _Local ` ,然后再去调用父类` _State_manager ` 的成员函数 ` _Get_value() ` 获取值并返回。而局部对象 ` _Local ` 在函数结束时析构。这意味着当前对象(` *this ` )失去共享状态,并且状态被完全销毁。
655
+
656
+ ` _Get_value() ` :
657
+
658
+ ``` cpp
659
+ _Ty& _Get_value () const {
660
+ if (!valid()) {
661
+ _Throw_future_error2 (future_errc::no_state);
662
+ }
663
+
664
+ return _Assoc_state->_Get_value(_Get_only_once);
665
+ }
666
+ ```
667
+
668
+ 先进行一下状态判断,如果拥有共享状态则继续,调用 ` _Assoc_state ` 的成员函数 ` _Get_value ` ,传递 ` _Get_only_once ` 参数,其实就是代表这个成员函数只能调用一次,次参数是里面进行状态判断的而已。
669
+
670
+ ` _Assoc_state ` 的类型是 ` _Associated_state<_Ty>* ` ,是一个指针类型,它实际会指向自己的子类对象,我们在讲 ` std::async ` 源码的时候提到了,它必然指向 ` _Deferred_async_state ` 或者 ` _Task_async_state ` 。
671
+
672
+ ` _Assoc_state->_Get_value ` 这其实是个多态调用,父类有这个虚函数:
673
+
674
+ ``` cpp
675
+ virtual _Ty& _Get_value (bool _ Get_only_once) {
676
+ unique_lock<mutex > _ Lock(_ Mtx);
677
+ if (_ Get_only_once && _ Retrieved) {
678
+ _ Throw_future_error2(future_errc::future_already_retrieved);
679
+ }
680
+
681
+ if (_Exception) {
682
+ _STD rethrow_exception(_Exception);
683
+ }
684
+
685
+ // TRANSITION: `_Retrieved` should be assigned before `_Exception` is thrown so that a `future::get`
686
+ // that throws a stored exception invalidates the future (N4950 [futures.unique.future]/17)
687
+ _Retrieved = true;
688
+ _Maybe_run_deferred_function(_Lock);
689
+ while (!_Ready) {
690
+ _Cond.wait(_Lock);
691
+ }
692
+
693
+ if (_Exception) {
694
+ _STD rethrow_exception(_Exception);
695
+ }
696
+
697
+ if constexpr (is_default_constructible_v<_Ty>) {
698
+ return _Result;
699
+ } else {
700
+ return _Result._Held_value;
701
+ }
702
+ }
703
+
704
+ ```
705
+
706
+ 但是子类 `_Task_async_state` 进行了重写,以 `launch::async` 策略创建的 future,那么实际会调用 `_Task_async_state::_Get_value` :
707
+
708
+ ```cpp
709
+ _State_type& _Get_value(bool _Get_only_once) override {
710
+ // return the stored result or throw stored exception
711
+ _Task.wait();
712
+ return _Mybase::_Get_value(_Get_only_once);
713
+ }
714
+ ```
715
+
716
+ > ` _Deferred_async_state ` 则没有进行重写,就是直接调用父类虚函数。
717
+
718
+ ` _Task ` 就是 ` ::Concurrency::task<void> _Task; ` ,调用 ` wait() ` 成员函数确保任务执行完毕。
719
+
720
+ ` _Mybase::_Get_value(_Get_only_once) ` 其实又是回去调用父类的虚函数了。
721
+
722
+ > ### ` _Get_value ` 方法详细解释
723
+
724
+ 1 . ** 状态检查** :
725
+ - 如果` _Get_only_once ` 为真并且结果已被检索过,则抛出` future_already_retrieved ` 异常。
726
+ 2 . ** 异常处理** :
727
+ - 如果存在存储的异常,重新抛出该异常。
728
+ 3 . ** 标记结果已被检索** :
729
+ - 将` _Retrieved ` 设置为` true ` 。
730
+ 4 . ** 执行延迟函数** :
731
+ - 调用` _Maybe_run_deferred_function ` 来运行可能的延迟任务。这个函数很简单,就是单纯的执行延时任务而已,在讲述 ` wait ` 成员函数的时候已经讲完了。
732
+ 5 . ** 等待结果就绪** :
733
+ - 如果结果尚未准备好,等待条件变量通知结果已就绪。(这里和 ` std::async ` 和 ` std::future ` 的组合无关,因为如果是 ` launch::async ` 模式创建的任务,重写的 ` _Get_value ` 是先调用了 ` _Task.wait(); ` 确保异步任务执行完毕,此处根本无需等待它)
734
+ 6 . ** 再次检查异常** :
735
+ - 再次检查是否有存储的异常,并重新抛出它。
736
+ 7 . ** 返回结果** :
737
+ - 如果` _Ty ` 是默认可构造的,返回结果` _Result ` 。
738
+ - 否则,返回` _Result._Held_value ` 。
739
+
740
+ ` _Result ` 是通过执行 ` _Call_immediate ` 函数,然后 ` _Call_immediate ` 再执行 ` _Set_value ` ,` _Set_value ` 再执行 ` _Emplace_result ` ,` _Emplace_result ` 再执行 ` _Emplace_result ` 获取到我们执行任务的值的。以 ` Ty ` 的偏特化为例:
741
+
742
+ ``` cpp
743
+ // _Packaged_state
744
+ void _Call_immediate (_ ArgTypes... _ Args) {
745
+ _ TRY_BEGIN
746
+ // 调用函数对象并捕获异常 传递返回值
747
+ this->_ Set_value(_ Fn(_ STD forward<_ ArgTypes>(_ Args)...), false);
748
+ _ CATCH_ALL
749
+ // 函数对象抛出异常就记录
750
+ this->_ Set_exception(_ STD current_exception(), false);
751
+ _ CATCH_END
752
+ }
753
+
754
+ // _ Asscoiated_state
755
+ void _ Set_value(const _ Ty& _ Val, bool _ At_thread_exit) { // store a result
756
+ unique_lock<mutex > _ Lock(_ Mtx);
757
+ _ Set_value_raw(_ Val, &_ Lock, _ At_thread_exit);
758
+ }
759
+ void _ Set_value_raw(const _ Ty& _ Val, unique_lock<mutex >* _ Lock, bool _ At_thread_exit) {
760
+ // store a result while inside a locked block
761
+ if (_ Already_has_stored_result()) {
762
+ _ Throw_future_error2(future_errc::promise_already_satisfied);
763
+ }
764
+
765
+ _Emplace_result(_Val);
766
+ _Do_notify(_Lock, _At_thread_exit);
767
+ }
768
+ template <class _Ty2 >
769
+ void _ Emplace_result(_ Ty2&& _ Val) {
770
+ // TRANSITION, incorrectly assigns _ Result when _ Ty is default constructible
771
+ if constexpr (is_default_constructible_v<_ Ty>) {
772
+ _ Result = _ STD forward<_ Ty2>(_ Val); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
773
+ } else {
774
+ ::new (static_cast<void* >(_ STD addressof(_ Result._ Held_value))) _ Ty(_ STD forward<_ Ty2>(_ Val));
775
+ _ Has_stored_result = true;
776
+ }
777
+ }
778
+ ```
779
+
780
+ ## 总结
781
+
782
+ 好了,到此也就可以了。
783
+
784
+ 你不会期待我们将每一个成员函数都分析一遍吧?首先是没有必要,其次是篇幅限制。
785
+
786
+ `std::future` 的继承关系让人感到头疼,但是如果耐心的看了一遍,全部搞明白了继承关系, `std::async` 如何创建的 `std::future` 也就没有问题了。
787
+
788
+ 其实各位不用着急完全理解,可以慢慢看,至少有许多的显著的信息,比如:
789
+
790
+ 1. `sttd::future` 的很多部分,如 `get()` 成员函数实现中,实际使用了虚函数。
791
+ 2. `std::async` 创建 `std::future` 对象中,内部其实也有父类指针指向子类对象,以及多态调用。
792
+ 3. `std::async` 的非延迟执行策略,使用到了自家的 PPL 库。
793
+ 4. 微软的 `std::async` 策略实现并不符合标准,不区分 `launch::async | launch::deferred` 和 `launch::async`。
794
+ 5. `std::future` 内部使用到了互斥量、条件变量、异常指针等设施。
0 commit comments