Skip to content

Commit 0b7e481

Browse files
committed
修改完善线程池的内容 #12
1. fix 线程池的实现中存在错误 #24 2. 修改示例的打印输出,增加线程 id
1 parent f2d31f4 commit 0b7e481

File tree

1 file changed

+48
-64
lines changed

1 file changed

+48
-64
lines changed

md/详细分析/04线程池.md

Lines changed: 48 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,16 @@ graph TD
9696
#include <boost/asio.hpp>
9797
#include <iostream>
9898

99+
std::mutex m;
100+
99101
void print_task(int n) {
100-
std::cout << "Task " << n << " is running." << std::endl;
102+
std::lock_guard<std::mutex> lc{ m };
103+
std::cout << "Task " << n << " is running on thr: " <<
104+
std::this_thread::get_id() << '\n';
101105
}
102106

103107
int main() {
104-
boost::asio::thread_pool pool{4}; // 创建一个包含 4 个线程的线程池
108+
boost::asio::thread_pool pool{ 4 }; // 创建一个包含 4 个线程的线程池
105109

106110
for (int i = 0; i < 10; ++i) {
107111
boost::asio::post(pool, [i] { print_task(i); });
@@ -111,7 +115,7 @@ int main() {
111115
}
112116
```
113117
114-
> [运行](https://godbolt.org/z/Pa3z1oYej)测试。
118+
> [运行](https://godbolt.org/z/41445Kab5)测试。
115119
116120
- 创建线程池时,指定线程数量,线程池会创建对应数量的线程。
117121
@@ -163,11 +167,11 @@ thread_pool::~thread_pool()
163167
boost::asio::thread_pool pool{ 4 };
164168

165169
for (int i = 0; i < 10; ++i) {
166-
boost::asio::post(pool, [i]() { print_task(i); });
170+
boost::asio::post(pool, [i] { print_task(i); });
167171
}
168172
```
169173

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

@@ -345,17 +349,13 @@ public:
345349
start();
346350
}
347351

348-
~ThreadPool(){
352+
~ThreadPool() {
349353
stop();
350-
join();
351354
}
352355

353356
void stop() {
354357
stop_.store(true);
355358
cv_.notify_all();
356-
}
357-
358-
void join(){
359359
for (auto& thread : pool_) {
360360
if (thread.joinable()) {
361361
thread.join();
@@ -383,16 +383,16 @@ public:
383383
return ret;
384384
}
385385

386-
void start(){
387-
for (std::size_t i = 0; i < num_threads_; ++i){
386+
void start() {
387+
for (std::size_t i = 0; i < num_threads_; ++i) {
388388
pool_.emplace_back([this] {
389389
while (!stop_) {
390390
Task task;
391391
{
392392
std::unique_lock<std::mutex> lc{ mutex_ };
393+
cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
393394
if (tasks_.empty())
394395
return;
395-
cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
396396
task = std::move(tasks_.front());
397397
tasks_.pop();
398398
}
@@ -415,89 +415,77 @@ private:
415415
**测试 demo**
416416

417417
```cpp
418-
int print_task(int n) {
419-
std::osyncstream{ std::cout } << "Task " << n << " is running." << std::endl;
420-
return n;
421-
}
422-
int print_task2(int n) {
423-
std::osyncstream{ std::cout } << "🐢🐢🐢 " << n << " 🐉🐉🐉" << std::endl;
424-
return n;
425-
}
426-
427418
int main() {
428-
ThreadPool pool{ 4 }; // 创建一个有 4 个线程的线程池 构造函数自动启动线程池
419+
ThreadPool pool{ 4 }; // 创建一个有 4 个线程的线程池
429420
std::vector<std::future<int>> futures; // future 集合,获取返回值
430421

431422
for (int i = 0; i < 10; ++i) {
432423
futures.emplace_back(pool.submit(print_task, i));
433424
}
434-
pool.join(); // 阻塞,让任务全部执行完毕
435-
436-
std::puts("---------------------");
437-
438-
pool.start(); // 重新启动线程池
439425

440426
for (int i = 0; i < 10; ++i) {
441427
futures.emplace_back(pool.submit(print_task2, i));
442428
}
443-
pool.join(); // 阻塞,让任务全部执行完毕
444429

445430
int sum = 0;
446-
for(auto& future : futures){
447-
sum += future.get();
431+
for (auto& future : futures) {
432+
sum += future.get(); // get() 成员函数 阻塞到任务执行完毕,获取返回值
448433
}
449434
std::cout << "sum: " << sum << '\n';
450-
} // 析构自动 stop() join()
435+
} // 析构自动 stop()
451436
```
452437

453-
**可能的[运行结果](https://godbolt.org/z/3rbExqbb7)**:
438+
**可能的[运行结果](https://godbolt.org/z/n7Tana59x)**
454439

455440
```shell
456-
Task 0 is running.
457-
Task 4 is running.
458-
Task 5 is running.
459-
Task 6 is running.
460-
Task 7 is running.
461-
Task 8 is running.
462-
Task 9 is running.
463-
Task 2 is running.
464-
Task 3 is running.
465-
Task 1 is running.
466-
---------------------
467-
🐢🐢🐢 0 🐉🐉🐉
441+
Task 0 is running on thr: 6900
442+
Task 1 is running on thr: 36304
443+
Task 5 is running on thr: 36304
444+
Task 3 is running on thr: 6900
445+
Task 7 is running on thr: 6900
446+
Task 2 is running on thr: 29376
447+
Task 6 is running on thr: 36304
448+
Task 4 is running on thr: 31416
468449
🐢🐢🐢 1 🐉🐉🐉
450+
Task 9 is running on thr: 29376
451+
🐢🐢🐢 0 🐉🐉🐉
452+
Task 8 is running on thr: 6900
469453
🐢🐢🐢 2 🐉🐉🐉
470-
🐢🐢🐢 5 🐉🐉🐉
454+
🐢🐢🐢 6 🐉🐉🐉
471455
🐢🐢🐢 4 🐉🐉🐉
456+
🐢🐢🐢 5 🐉🐉🐉
472457
🐢🐢🐢 3 🐉🐉🐉
473-
🐢🐢🐢 6 🐉🐉🐉
474458
🐢🐢🐢 7 🐉🐉🐉
475459
🐢🐢🐢 8 🐉🐉🐉
476460
🐢🐢🐢 9 🐉🐉🐉
477461
sum: 90
478462
```
479463

480-
> 如果不自己显式调用 `join()` ,而是等待线程池对象调用析构函数,那么效果如同 `asio::thread_pool`,会先进行 `stop`导致一些任务无法执行
464+
> 如果等待线程池对象调用析构函数,那么效果如同 `asio::thread_pool`,会先进行 `stop`这可能导致一些任务无法执行。不过我们在最后**循环遍历了 `futures`**,调用 `get()` 成员函数,不存在这个问题
481465
482466
它支持**任意可调用类型**,当然也包括非静态成员函数。我们使用了 [`std::decay_t`](https://zh.cppreference.com/w/cpp/types/decay),所以参数的传递其实是**按值复制**,而不是引用传递,这一点和大部分库的设计一致。示例如下:
483467

484468
```cpp
485-
struct X{
486-
void f(const int& n)const{
487-
std::cout << &n << '\n';
469+
struct X {
470+
void f(const int& n) const {
471+
std::osyncstream{ std::cout } << &n << '\n';
488472
}
489473
};
490474

491-
X x;
492-
int n = 6;
493-
std::cout << &n << '\n';
494-
pool.start();
495-
pool.submit(&X::f, &x, n); // 默认复制,地址不同
496-
pool.submit(&X::f, &x, std::ref(n));
497-
pool.join();
475+
int main() {
476+
ThreadPool pool{ 4 }; // 创建一个有 4 个线程的线程池
477+
478+
X x;
479+
int n = 6;
480+
std::cout << &n << '\n';
481+
auto t = pool.submit(&X::f, &x, n); // 默认复制,地址不同
482+
auto t2 = pool.submit(&X::f, &x, std::ref(n));
483+
t.wait();
484+
t2.wait();
485+
} // 析构自动 stop()
498486
```
499487
500-
> [运行](https://godbolt.org/z/vTc7M8Kov)测试。
488+
> [运行](https://godbolt.org/z/vY458T44e)测试。
501489
502490
我们的线程池的 `submit` 成员函数在传递参数的行为上,与先前介绍的 `std::thread` 和 `std::async` 等设施基本一致。
503491
@@ -512,11 +500,7 @@ pool.join();
512500
**外部接口:**
513501
514502
- **`stop()`**:停止线程池,通知所有线程退出(不会等待所有任务执行完毕)。
515-
516-
- **`join()`**:等待所有线程完成任务。
517-
518-
- **`submit()`**:将任务提交到任务队列,并返回一个`std::future`对象用于获取任务结果。
519-
503+
- **`submit()`**:将任务提交到任务队列,并返回一个`std::future`对象用于获取任务结果以及确保任务执行完毕。
520504
- **`start()`**:启动线程池,创建并启动指定数量的线程。
521505
522506
我们并没有提供一个功能强大的所谓的“***调度器***”,我们只是利用条件变量和互斥量,让操作系统自行调度而已,它并不具备设置任务优先级之类的调度功能。

0 commit comments

Comments
 (0)