@@ -722,7 +722,7 @@ auto sum(ForwardIt first, ForwardIt last) {
722722
723723> [运行](https://godbolt.org/z/r19MYcv6e)测试。
724724
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()` 获取任务的返回值加起来即可。
726726
727727到此,也就可以了。
728728
@@ -799,7 +799,7 @@ int main() {
799799来自线程的异常: 一个异常
800800```
801801
802- 你可能对这段代码还有一些疑问:我们写的是 ` promised <int>` ,但是却没有使用 ` set_value ` 设置值,你可能会想着再写一行 ` prom.set_value(0) ` ?
802+ 你可能对这段代码还有一些疑问:我们写的是 ` promise <int>` ,但是却没有使用 ` set_value ` 设置值,你可能会想着再写一行 ` prom.set_value(0) ` ?
803803
804804共享状态的 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 ` 对象只能是存储值或者异常其中一种,而** 无法共存** 。
805805
@@ -893,13 +893,43 @@ _Ty& get() {
893893
894894如果需要进行多次 ` get ` 调用,可以考虑使用下文提到的 ` std::shared_future ` 。
895895
896- ### 多个线程的等待
896+ ### 多个线程的等待 ` std::shared_future `
897897
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` 一样。
899929
900930`std::future` 是只能移动的,其所有权可以在不同的对象中互相传递,但只有一个对象可以获得特定的同步结果。而 `std::shared_future` 是可复制的,多个对象可以指代同一个共享状态。
901931
902- 在多个线程中对** 同一个 ** ` std::shared_future ` 对象进行操作时(如果没有进行同步保护)存在竞争条件 。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 ` shared_future ` 对象** 副本** 进行访问,则是安全的。
932+ 在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在条件竞争 。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 `shared_future` 对象**副本**进行访问,则是安全的。
903933
904934```cpp
905935std::string fetch_data() {
@@ -932,7 +962,7 @@ int main() {
932962}
933963```
934964
935- 这段代码存在数据竞争,就如同我们先前所说:“*** 在多个线程中对** 同一个 ** ` std::shared_future ` 对象进行操作时(如果没有进行同步保护)存在竞争条件 *** ”,它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是“** 同一个** ”进行操作了。可以改为:
965+ 这段代码存在数据竞争,就如同我们先前所说:“*** 在多个线程中对** 同一个 ** ` std::shared_future ` 对象进行操作时(如果没有进行同步保护)存在条件竞争 *** ”,它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是“** 同一个** ”进行操作了。可以改为:
936966
937967``` cpp
938968std::string fetch_data () {
@@ -962,13 +992,13 @@ int main() {
962992}
963993```
964994
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 ] 。
966996
967997` 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 ` 的所有权。
968998
969999``` 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() }; // 隐式转移所有权
9721002```
9731003
9741004就不需要再强调了。
@@ -1010,7 +1040,7 @@ class duration;
10101040如你所见,它默认的时钟节拍是 1,这是一个很重要的类,标准库通过它定义了很多的时间类型,比如 ** ` std::chrono::minutes ` ** 是分钟类型,那么它的 ` Period ` 就是 ` std::ratio<60> ` ,因为一分钟等于 60 秒。
10111041
10121042``` cpp
1013- std::chrono:: minutes std::chrono::duration< /* int29 */ , std:: ratio<60 >>
1043+ using minutes = duration< int , ratio<60 >>;
10141044```
10151045
10161046稳定时钟(Steady Clock)是指提供稳定、持续递增的时间流逝信息的时钟。它的特点是不受系统时间调整或变化的影响,即使在系统休眠或时钟调整的情况下,它也能保持稳定。在 C++ 标准库中,[ ` std::chrono::steady_clock ` ] ( https://zh.cppreference.com/w/cpp/chrono/steady_clock ) 就是一个稳定时钟。它通常用于测量时间间隔和性能计时等需要高精度和稳定性的场景。可以通过 ` is_steady ` 静态常量判断当前时钟是否是稳定时钟。
0 commit comments