From 3078c28547002d3084f11f17abb8b824c5682143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?mq=E7=99=BD?= <3326284481@qq.com> Date: Thu, 1 Aug 2024 18:19:21 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E4=BF=AE=E6=94=B9=E7=94=A8=E8=AF=8D?= =?UTF-8?q?=E2=80=9C=E5=A0=B5=E5=A1=9E=E2=80=9D->"=E9=98=BB=E5=A1=9E"?= =?UTF-8?q?=E3=80=82=202.=20=E7=A8=8D=E5=BE=AE=E4=BF=AE=E6=94=B9=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E6=B1=A0=E4=B8=80=E7=AB=A0=E4=B8=AD=E7=9A=84=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=8F=8F=E8=BF=B0=E3=80=82#12=203.=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=8D=90=E8=B5=A0=E9=A1=B5=E9=9D=A2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- ...344\275\277\347\224\250\347\272\277\347\250\213.md" | 6 +++--- ...345\220\214\346\255\245\346\223\215\344\275\234.md" | 8 ++++---- .../04\347\272\277\347\250\213\346\261\240.md" | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 76ce16b4..7e926e45 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@
-![猫猫虫](/image/猫猫虫旋转.jpg) +![猫猫虫](./image/猫猫虫旋转.jpg) 如果你觉得本仓库对你有所帮助,可以通过支付宝赞助白老师,激励白老师有更多的精力和信心维护本仓库。 @@ -37,4 +37,4 @@ > [!TIP] > 每一位开发者赞助 `30`,白老师一天的食品安全就有了着落。 -![赞助](/image/赞助.jpg) +cpp diff --git "a/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" "b/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" index 3abff876..898b388f 100644 --- "a/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" +++ "b/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" @@ -36,7 +36,7 @@ int main(){ `std::thread t{ hello };` 创建了一个线程对象 `t`,将 `hello` 作为它的[可调用(Callable)](https://zh.cppreference.com/w/cpp/named_req/Callable)对象,在新线程中执行。线程对象关联了一个线程资源,我们无需手动控制,在线程对象构造成功,就自动在新线程开始执行函数 `hello`。 -`t.join();` 等待线程对象 `t` 关联的线程执行完毕,否则将一直堵塞。这里的调用是必须的,否则 `std::thread` 的析构函数将调用 [`std::terminate()`](https://zh.cppreference.com/w/cpp/error/terminate) 无法正确析构。 +`t.join();` 等待线程对象 `t` 关联的线程执行完毕,否则将一直阻塞。这里的调用是必须的,否则 `std::thread` 的析构函数将调用 [`std::terminate()`](https://zh.cppreference.com/w/cpp/error/terminate) 无法正确析构。 这是因为我们创建线程对象 `t` 的时候就关联了一个活跃的线程,调用 `join()` 就是确保线程对象关联的线程已经执行完毕,然后会修改对象的状态,让 [`std::thread::joinable()`](https://zh.cppreference.com/w/cpp/thread/thread/joinable) 返回 `false`,表示线程对象目前没有关联活跃线程。`std::thread` 的析构函数,正是通过 `joinable()` 判断线程对象目前是否有关联活跃线程,如果为 `true`,那么就当做有关联活跃线程,会调用 `std::terminate()`。 @@ -291,7 +291,7 @@ my_thread.join(); 认为这样可以确保被分离的线程在这里阻塞执行完? -我们前面聊的很清楚了,detach() 是线程分离,**线程对象放弃了线程资源的所有权**,此时我们的 my_thread 它现在根本没有关联任何线程。调用 join() 是:“阻塞当前线程直至 *this 所标识的线程结束其执行”,我们的**线程对象都没有线程,堵塞什么?执行什么呢?** +我们前面聊的很清楚了,detach() 是线程分离,**线程对象放弃了线程资源的所有权**,此时我们的 my_thread 它现在根本没有关联任何线程。调用 join() 是:“阻塞当前线程直至 *this 所标识的线程结束其执行”,我们的**线程对象都没有线程,阻塞什么?执行什么呢?** 简单点说,必须是 std::thread 的 joinable() 为 true 即线程对象有活跃线程,才能调用 join() 和 detach()。 @@ -691,7 +691,7 @@ int main() { } ``` -这段代码通过**移动构造**转移了线程对象 `t` 的线程资源所有权到 `t2`,这里虽然有两个 `std::thread` 对象,但是从始至终只有一个线程资源,让持有线程资源的 `t2` 对象最后调用 `join()` 堵塞让其线程执行完毕。`t` 与 `t2` 都能正常析构。 +这段代码通过**移动构造**转移了线程对象 `t` 的线程资源所有权到 `t2`,这里虽然有两个 `std::thread` 对象,但是从始至终只有一个线程资源,让持有线程资源的 `t2` 对象最后调用 `join()` 阻塞让其线程执行完毕。`t` 与 `t2` 都能正常析构。 我们还可以使用移动赋值来转移线程资源的所有权: diff --git "a/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" "b/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" index 08f2bd1c..240529c1 100644 --- "a/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" +++ "b/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" @@ -446,14 +446,14 @@ int main(){ 其实到此基本就差不多了,我们再介绍两个常见问题即可: -1. 如果从 `std::async` 获得的 [std::future](https://zh.cppreference.com/w/cpp/thread/future) 没有被移动或绑定到引用,那么在完整表达式结尾, [std::future](https://zh.cppreference.com/w/cpp/thread/future) 的**析构函数将阻塞到异步计算完成**。因为临时对象的生存期就在这一行,调用析构函数阻塞执行。 +1. 如果从 `std::async` 获得的 [`std::future`](https://zh.cppreference.com/w/cpp/thread/future) 没有被移动或绑定到引用,那么在完整表达式结尾, `std::future` 的**[析构函数](https://zh.cppreference.com/w/cpp/thread/future/%7Efuture)将阻塞,直到到异步任务完成**。因为临时对象的生存期就在这一行,而对象生存期结束就会调用调用析构函数。 ```cpp std::async(std::launch::async, []{ f(); }); // 临时量的析构函数等待 f() std::async(std::launch::async, []{ g(); }); // f() 完成前不开始 ``` - 如你所见,这并不能创建异步任务,会堵塞,然后逐个执行。 + 如你所见,这并不能创建异步任务,它会阻塞,然后逐个执行。 2. 被移动的 `std::future` 没有所有权,失去共享状态,不能调用 `get`、`wait` 成员函数。 @@ -489,7 +489,7 @@ std::packaged_task task([](int a, int b){ }); std::futurefuture = task.get_future(); task(10, 2); // 此处执行任务 -std::cout << future.get() << '\n'; // 不堵塞,此处获取返回值 +std::cout << future.get() << '\n'; // 不阻塞,此处获取返回值 ``` > [运行](https://godbolt.org/z/799Khvadc)测试。 @@ -505,7 +505,7 @@ std::thread t{ std::move(task),10,2 }; // 任务在线程中执行 // todo.. 幻想还有许多耗时的代码 t.join(); -std::cout << future.get() << '\n'; // 并不堵塞,获取任务返回值罢了 +std::cout << future.get() << '\n'; // 并不阻塞,获取任务返回值罢了 ``` > [运行](https://godbolt.org/z/85r9db49z)测试。 diff --git "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/04\347\272\277\347\250\213\346\261\240.md" "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/04\347\272\277\347\250\213\346\261\240.md" index 9a66f839..4fe0805a 100644 --- "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/04\347\272\277\347\250\213\346\261\240.md" +++ "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/04\347\272\277\347\250\213\346\261\240.md" @@ -72,7 +72,7 @@ graph TD --- -了解以上这些基础概念是第一步也是最后一步,随着水平的提升,对这些概念认知与理解也会逐渐提升。 +了解以上这些基础概念是第一步也是最后一步,随着水平的提升,对这些概念的理解也会逐渐提升。 ## 市面上常见的线程池 @@ -169,7 +169,7 @@ for (int i = 0; i < 10; ++i) { > [运行](https://godbolt.org/z/haPqKb1h7)测试。 -因为析构函数并不是堵塞执行完所有任务,而是先**停止**,再 `join()` 以及 `shutdown()`。 +因为析构函数并不是阻塞直到执行完所有任务,而是先**停止**,再 `join()` 以及 `shutdown()`。 `Boost.Asio` 提供的线程池使用十分简单,接口高度封装,几乎无需关心底层具体实现,易于使用。 @@ -450,7 +450,7 @@ int main() { } // 析构自动 stop() join() ``` -**可能的[运行结果](https://godbolt.org/z/YT4ahh1Wz)**: +**可能的[运行结果](https://godbolt.org/z/3rbExqbb7)**: ```shell Task 0 is running. @@ -495,11 +495,11 @@ sum: 90 - **`start()`**:启动线程池,创建并启动指定数量的线程。 ---- +我们并没有提供一个功能强大的所谓的“***调度器***”,我们只是利用条件变量和互斥量,让操作系统自行调度而已,它并不具备设置任务优先级之类的调度功能。 当然,你可能还希望我们的线程池具备更多功能或改进,比如控制任务优先级、设置最大线程数量、返回当前活跃线程数等。此外,异常处理也是一个值得考虑的方面。 -有些功能实现起来非常简单,而有些则需要更多的思考和设计。不过,这些优化超出了本次讲解的范围。如果有兴趣,可以尝试自行优化我们提供的线程池实现。我们给出的线程池实现简单完善且直观,用来学习再好不过。 +有些功能实现起来非常简单,而有些则需要更多的思考和设计。不过,这些功能超出了本次讲解的范围。如果有兴趣,可以尝试自行优化我们提供的线程池实现,添加更多的功能。我们给出的线程池实现简单完善且直观,用来学习再好不过。 ## 总结