Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0b7e481

Browse files
committed
修改完善线程池的内容 Mq-b#12
1. fix 线程池的实现中存在错误 Mq-b#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)

AltStyle によって変換されたページ (->オリジナル) /