@@ -722,7 +722,7 @@ auto sum(ForwardIt first, ForwardIt last) {
722
722
723
723
> [运行](https://godbolt.org/z/r19MYcv6e)测试。
724
724
725
- 相比于之前,其实不同无非是定义了 `std::vector<std::packaged_task<value_type()>> tasks` 与 `std::vector<std::future<value_type>> futures` ,然后在循环中制造任务插入容器,关联 tuple ,再放到线程中执行。最后汇总的时候写一个循环,`futures[i].get()` 获取任务的返回值加起来即可。
725
+ 相比于之前,其实不同无非是定义了 `std::vector<std::packaged_task<value_type()>> tasks` 与 `std::vector<std::future<value_type>> futures` ,然后在循环中制造任务插入容器,关联 future ,再放到线程中执行。最后汇总的时候写一个循环,`futures[i].get()` 获取任务的返回值加起来即可。
726
726
727
727
到此,也就可以了。
728
728
@@ -799,7 +799,7 @@ int main() {
799
799
来自线程的异常: 一个异常
800
800
```
801
801
802
- 你可能对这段代码还有一些疑问:我们写的是 ` promised <int>` ,但是却没有使用 ` set_value ` 设置值,你可能会想着再写一行 ` prom.set_value(0) ` ?
802
+ 你可能对这段代码还有一些疑问:我们写的是 ` promise <int>` ,但是却没有使用 ` set_value ` 设置值,你可能会想着再写一行 ` prom.set_value(0) ` ?
803
803
804
804
共享状态的 promise 已经存储值或者异常,再次调用 ` set_value ` (` set_exception ` ) 会抛出 [ std::future_error] ( https://zh.cppreference.com/w/cpp/thread/future_error ) 异常,将错误码设置为 [ ` promise_already_satisfied ` ] ( https://zh.cppreference.com/w/cpp/thread/future_errc ) 。这是因为 ` std::promise ` 对象只能是存储值或者异常其中一种,而** 无法共存** 。
805
805
@@ -893,13 +893,43 @@ _Ty& get() {
893
893
894
894
如果需要进行多次 ` get ` 调用,可以考虑使用下文提到的 ` std::shared_future ` 。
895
895
896
- ### 多个线程的等待
896
+ ### 多个线程的等待 ` std::shared_future `
897
897
898
- 之前的例子中都在用 ` std::future ` ,不过 ` std::future ` 也有局限性。很多线程在等待的时候,只有一个线程能获取结果。当多个线程等待相同事件的结果时,就需要使用 ` std::shared_future ` 来替代 ` std::future ` 了。` std::future ` 与 ` std::shared_future ` 的区别就如同 ` std::unique_ptr ` 、` std::shared_ptr ` 一样。
898
+ 之前的例子中我们一直使用 ` std::future ` ,但 ` std::future ` 有一个局限:** future 是一次性的** ,它的结果只能被一个线程获取。` get() ` 成员函数只能调用一次,当结果被某个线程获取后,` std::future ` 就无法再用于其他线程。
899
+
900
+ ``` cpp
901
+ int task (){
902
+ // todo..
903
+ return 10;
904
+ }
905
+
906
+ void thread_functio (std::future<int >& fut){
907
+ // todo..
908
+ int result = fut.get();
909
+ std::cout << result << '\n';
910
+ // todo..
911
+ }
912
+
913
+ int main(){
914
+ auto future = std::async(task); // 启动耗时的异步任务
915
+
916
+ // 可能有多个线程都需要此任务的返回值,于是我们将与其关联的 future 对象的引入传入
917
+ std::thread t{ thread_functio,std::ref(future) };
918
+ std::thread t2{ thread_functio,std::ref(future) };
919
+ t.join();
920
+ t2.join();
921
+ }
922
+ ```
923
+
924
+ > 可能有多个线程都需要耗时的异步任务的返回值,于是我们将与其关联的 future 对象的引入传给线程对象,让它能在需要的时候获取。
925
+ >
926
+ > 但是这存在个问题,future 是一次性的,只能被调用一次 `get()` 成员函数,所以以上代码存在问题。
927
+
928
+ 此时就需要使用 `std::shared_future` 来替代 `std::future` 了。`std::future` 与 `std::shared_future` 的区别就如同 `std::unique_ptr`、`std::shared_ptr` 一样。
899
929
900
930
`std::future` 是只能移动的,其所有权可以在不同的对象中互相传递,但只有一个对象可以获得特定的同步结果。而 `std::shared_future` 是可复制的,多个对象可以指代同一个共享状态。
901
931
902
- 在多个线程中对** 同一个 ** ` std::shared_future ` 对象进行操作时(如果没有进行同步保护)存在竞争条件 。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 ` shared_future ` 对象** 副本** 进行访问,则是安全的。
932
+ 在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在条件竞争 。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 `shared_future` 对象**副本**进行访问,则是安全的。
903
933
904
934
```cpp
905
935
std::string fetch_data() {
@@ -932,7 +962,7 @@ int main() {
932
962
}
933
963
```
934
964
935
- 这段代码存在数据竞争,就如同我们先前所说:“*** 在多个线程中对** 同一个 ** ` std::shared_future ` 对象进行操作时(如果没有进行同步保护)存在竞争条件 *** ”,它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是“** 同一个** ”进行操作了。可以改为:
965
+ 这段代码存在数据竞争,就如同我们先前所说:“*** 在多个线程中对** 同一个 ** ` std::shared_future ` 对象进行操作时(如果没有进行同步保护)存在条件竞争 *** ”,它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是“** 同一个** ”进行操作了。可以改为:
936
966
937
967
``` cpp
938
968
std::string fetch_data () {
@@ -962,13 +992,13 @@ int main() {
962
992
}
963
993
```
964
994
965
- 这样访问的就都是 ` std::shared_future ` 的副本了,我们的 lambda 按复制捕获 std::shared_future 对象,每个线程都有一个 shared_future 的副本,这样不会有任何问题。这一点和 ` std::shared_ptr ` 类似[ ^ 2 ] 。
995
+ 这样访问的就都是 ` std::shared_future ` 的副本了,我们的 lambda 按复制捕获 ` std::shared_future ` 对象,每个线程都有一个 shared_future 的副本,这样不会有任何问题。这一点和 ` std::shared_ptr ` 类似[ ^ 2 ] 。
966
996
967
997
` std::promise ` 也同,它的 ` get_future() ` 成员函数一样可以用来构造 ` std::shared_future ` ,虽然它的返回类型是 ` std::future ` ,不过不影响,这是因为 ` std::shared_future ` 有一个 ` std::future<T>&& ` 参数的[ 构造函数] ( https://zh.cppreference.com/w/cpp/thread/shared_future/shared_future ) ,转移 ` std::future ` 的所有权。
968
998
969
999
``` cpp
970
- std::promise<std::string>p;
971
- std::shared_future<std::string>sf{ p.get_future() }; // 隐式转移所有权
1000
+ std::promise<std::string> p;
1001
+ std::shared_future<std::string> sf{ p.get_future() }; // 隐式转移所有权
972
1002
```
973
1003
974
1004
就不需要再强调了。
@@ -1010,7 +1040,7 @@ class duration;
1010
1040
如你所见,它默认的时钟节拍是 1,这是一个很重要的类,标准库通过它定义了很多的时间类型,比如 ** ` std::chrono::minutes ` ** 是分钟类型,那么它的 ` Period ` 就是 ` std::ratio<60> ` ,因为一分钟等于 60 秒。
1011
1041
1012
1042
``` cpp
1013
- std::chrono:: minutes std::chrono::duration< /* int29 */ , std:: ratio<60 >>
1043
+ using minutes = duration< int , ratio<60 >>;
1014
1044
```
1015
1045
1016
1046
稳定时钟(Steady Clock)是指提供稳定、持续递增的时间流逝信息的时钟。它的特点是不受系统时间调整或变化的影响,即使在系统休眠或时钟调整的情况下,它也能保持稳定。在 C++ 标准库中,[ ` std::chrono::steady_clock ` ] ( https://zh.cppreference.com/w/cpp/chrono/steady_clock ) 就是一个稳定时钟。它通常用于测量时间间隔和性能计时等需要高精度和稳定性的场景。可以通过 ` is_steady ` 静态常量判断当前时钟是否是稳定时钟。
0 commit comments