Skip to content

Commit

Permalink
1. 修改用词“堵塞”->"阻塞"。
Browse files Browse the repository at this point in the history
2. 稍微修改线程池一章中的部分描述。#12
3. 修改捐赠页面。
  • Loading branch information
Mq-b committed Aug 1, 2024
1 parent 6eb34a0 commit 3078c28
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 14 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

<div align="center">

![猫猫虫](/image/猫猫虫旋转.jpg)
![猫猫虫](./image/猫猫虫旋转.jpg)

如果你觉得本仓库对你有所帮助,可以通过支付宝赞助白老师,激励白老师有更多的精力和信心维护本仓库。

Expand All @@ -37,4 +37,4 @@
> [!TIP]
> 每一位开发者赞助 `30`,白老师一天的食品安全就有了着落。
![赞助](/image/赞助.jpg)
<img src="./image/赞助.jpg" width=512px alt="cpp"/>
6 changes: 3 additions & 3 deletions md/02使用线程.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()`

Expand Down Expand Up @@ -291,7 +291,7 @@ my_thread.join();

认为这样可以确保被分离的线程在这里阻塞执行完?

我们前面聊的很清楚了,detach() 是线程分离,**线程对象放弃了线程资源的所有权**,此时我们的 my_thread 它现在根本没有关联任何线程。调用 join() 是:“阻塞当前线程直至 *this 所标识的线程结束其执行”,我们的**线程对象都没有线程,堵塞什么?执行什么呢?**
我们前面聊的很清楚了,detach() 是线程分离,**线程对象放弃了线程资源的所有权**,此时我们的 my_thread 它现在根本没有关联任何线程。调用 join() 是:“阻塞当前线程直至 *this 所标识的线程结束其执行”,我们的**线程对象都没有线程,阻塞什么?执行什么呢?**

简单点说,必须是 std::thread 的 joinable() 为 true 即线程对象有活跃线程,才能调用 join() 和 detach()。

Expand Down Expand Up @@ -691,7 +691,7 @@ int main() {
}
```
这段代码通过**移动构造**转移了线程对象 `t` 的线程资源所有权到 `t2`,这里虽然有两个 `std::thread` 对象,但是从始至终只有一个线程资源,让持有线程资源的 `t2` 对象最后调用 `join()` 堵塞让其线程执行完毕`t``t2` 都能正常析构。
这段代码通过**移动构造**转移了线程对象 `t` 的线程资源所有权到 `t2`,这里虽然有两个 `std::thread` 对象,但是从始至终只有一个线程资源,让持有线程资源的 `t2` 对象最后调用 `join()` 阻塞让其线程执行完毕`t``t2` 都能正常析构。
我们还可以使用移动赋值来转移线程资源的所有权:
Expand Down
8 changes: 4 additions & 4 deletions md/04同步操作.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 成员函数。
Expand Down Expand Up @@ -489,7 +489,7 @@ std::packaged_task<double(int, int)> task([](int a, int b){
});
std::future<double>future = task.get_future();
task(10, 2); // 此处执行任务
std::cout << future.get() << '\n'; // 不堵塞,此处获取返回值
std::cout << future.get() << '\n'; // 不阻塞,此处获取返回值
```
> [运行](https://godbolt.org/z/799Khvadc)测试。
Expand All @@ -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)测试。
Expand Down
10 changes: 5 additions & 5 deletions md/详细分析/04线程池.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ graph TD

---

了解以上这些基础概念是第一步也是最后一步,随着水平的提升,对这些概念认知与理解也会逐渐提升
了解以上这些基础概念是第一步也是最后一步,随着水平的提升,对这些概念的理解也会逐渐提升

## 市面上常见的线程池

Expand Down Expand Up @@ -169,7 +169,7 @@ for (int i = 0; i < 10; ++i) {

> [运行](https://godbolt.org/z/haPqKb1h7)测试。
因为析构函数并不是堵塞执行完所有任务,而是先**停止**,再 `join()` 以及 `shutdown()`
因为析构函数并不是阻塞直到执行完所有任务,而是先**停止**,再 `join()` 以及 `shutdown()`

`Boost.Asio` 提供的线程池使用十分简单,接口高度封装,几乎无需关心底层具体实现,易于使用。

Expand Down Expand Up @@ -450,7 +450,7 @@ int main() {
} // 析构自动 stop() join()
```
**可能的[运行结果](https://godbolt.org/z/YT4ahh1Wz)**:
**可能的[运行结果](https://godbolt.org/z/3rbExqbb7)**:
```shell
Task 0 is running.
Expand Down Expand Up @@ -495,11 +495,11 @@ sum: 90

- **`start()`**:启动线程池,创建并启动指定数量的线程。

---
我们并没有提供一个功能强大的所谓的“***调度器***”,我们只是利用条件变量和互斥量,让操作系统自行调度而已,它并不具备设置任务优先级之类的调度功能。

当然,你可能还希望我们的线程池具备更多功能或改进,比如控制任务优先级、设置最大线程数量、返回当前活跃线程数等。此外,异常处理也是一个值得考虑的方面。

有些功能实现起来非常简单,而有些则需要更多的思考和设计。不过,这些优化超出了本次讲解的范围。如果有兴趣,可以尝试自行优化我们提供的线程池实现。我们给出的线程池实现简单完善且直观,用来学习再好不过。
有些功能实现起来非常简单,而有些则需要更多的思考和设计。不过,这些功能超出了本次讲解的范围。如果有兴趣,可以尝试自行优化我们提供的线程池实现,添加更多的功能。我们给出的线程池实现简单完善且直观,用来学习再好不过。

## 总结

Expand Down

0 comments on commit 3078c28

Please sign in to comment.