• 【面试心得】C++ 线程池总结


    什么是线程池

    线程池(Thread Pool)是一种多线程编程的设计模式,它用于管理和复用线程,以有效地执行并发任务。线程池由一组预创建的线程组成,这些线程在需要时被分配来执行任务。线程池的核心思想是将线程的创建、销毁和管理工作从任务执行中分离出来,从而提高性能、资源利用率和代码可维护性。

    线程池的主要目标是解决以下问题:

    1. 减少线程创建和销毁的开销: 在多线程应用程序中,频繁地创建和销毁线程会导致额外的开销,包括内存分配、上下文切换等。线程池在应用程序启动时创建一组线程,并在任务完成后不立即销毁它们,而是将它们保持在池中以供重用。

    2. 控制并发度: 线程池允许您限制同时执行的线程数量,从而控制系统的并发度。这有助于避免过多的并发线程导致系统资源不足或性能下降的问题。

    3. 提高系统的稳定性和可靠性: 通过管理线程的生命周期,线程池可以提高系统的稳定性,避免因线程创建失败或线程泄漏而导致的问题。

    4. 提高代码可维护性: 使用线程池将任务的执行与线程管理分离,使代码更清晰和可维护。您可以专注于任务的逻辑,而不必担心线程的创建和销毁。

    5. 提高性能: 线程池可以提高多核处理器的利用率,充分利用可用的计算资源,从而加速任务的执行。

    总之,线程池是一种有助于管理多线程应用程序的机制,它可以提高性能、资源利用率和代码可维护性,同时降低了多线程编程的复杂性。这就是为什么在许多多线程应用程序中使用线程池的原因。

    线程池工作原理

    1. 初始化: 在线程池启动时,会创建一定数量的线程。这些线程通常被称为工作线程。线程池还会创建一个任务队列,用于存储待执行的任务。

    2. 任务提交: 当应用程序有任务需要执行时,它可以将任务提交到线程池。这些任务通常表示为可调用的函数对象(函数指针、Lambda表达式等)。任务提交后,线程池将任务放入任务队列中等待执行。

    3. 任务调度: 线程池中的工作线程会循环地从任务队列中获取任务。一旦工作线程获取到任务,它就会执行该任务。如果没有可用任务,工作线程会等待,直到有任务可执行。

    4. 任务执行: 工作线程执行从任务队列中获取的任务。任务可以并行执行,因为线程池中有多个工作线程,每个工作线程都可以执行不同的任务。

    5. 任务完成: 任务执行完成后,工作线程可以继续从任务队列中获取下一个任务,或者根据线程池的策略决定是否保持线程处于等待状态以等待更多任务。

    6. 线程复用: 工作线程在执行完任务后不会立即销毁,而是返回线程池中等待下一个任务。这样可以减少线程创建和销毁的开销,并提高性能。

    7. 线程池管理: 线程池通常具有管理功能,可以控制线程的数量、动态调整线程池大小、处理异常、记录日志等。这些管理功能有助于线程池的稳定性和性能优化。

    8. 任务完成通知: 通常,线程池允许应用程序检测任务是否完成,并获取任务的返回结果。这可以通过各种机制实现,如回调函数、Future/Promise等。

    线程池的工作原理允许多个任务在一组工作线程上并发执行,从而提高了系统的性能和资源利用率。线程池还可以根据需要动态调整线程的数量,以适应不同的工作负载。这使得线程池成为处理并发任务的强大工具。

    保障线程池的线程安全

    1. 任务队列的互斥锁: 任务队列是线程池中的关键资源,因此需要使用互斥锁(Mutex)来保护它,确保只有一个线程能够访问或修改任务队列。这样可以避免多个线程同时向队列添加或删除任务而导致的问题。

    2. 条件变量: 除了互斥锁,使用条件变量(Condition Variable)来通知等待任务的线程有新任务可执行。条件变量允许线程等待某个条件满足后再继续执行,这在任务队列为空时非常有用。

    3. 线程池状态管理: 维护线程池的状态,例如标识线程池是否处于运行状态、是否正在销毁等。在操作线程池状态时需要加锁,以确保线程安全。

    4. 资源的合理共享: 确保线程池中的线程在共享资源时进行适当的同步。避免数据竞争问题,如多个线程同时修改同一个变量。

    5. 异常处理: 考虑线程中可能出现的异常情况,特别是在任务执行时,确保异常不会导致线程池崩溃。可以使用 try-catch 块捕获异常,记录日志或采取适当的措施。

    6. 资源管理: 确保线程池中的线程在退出时释放资源,如内存、文件句柄等。防止资源泄漏。

    7. 使用原子操作: 在需要对共享变量进行增减等操作时,使用原子操作,以确保操作的原子性,避免竞态条件。

    代码实现

    版本一

    xthread_pool.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. class XTask
    9. {
    10. public:
    11. virtual int Run() = 0;
    12. std::function<bool()> is_exit = nullptr;
    13. };
    14. class XThreadPool
    15. {
    16. public:
    17. //
    18. /// 初始化线程池
    19. /// @para num 线程数量
    20. void Init(int num);
    21. //
    22. /// 启动所有线程,必须先调用Init
    23. void Start();
    24. //
    25. /// 线程池退出
    26. void Stop();
    27. void AddTask(XTask* task);
    28. XTask* GetTask();
    29. //线程池是否退出
    30. bool is_exit() { return is_exit_; }
    31. int task_run_count() { return task_run_count_; }
    32. private:
    33. //线程池线程的入口函数
    34. void Run() ;
    35. int thread_num_ = 0;//线程数量
    36. std::mutex mux_;
    37. std::vector threads_;
    38. std::list tasks_;
    39. std::condition_variable cv_;
    40. bool is_exit_ = false; //线程池退出
    41. //正在运行的任务数量,线程安全
    42. std::atomic<int> task_run_count_ = {0};
    43. };

     xthread_pool.cpp

    1. #include "xthread_pool.h"
    2. #include
    3. using namespace std;
    4. //
    5. /// 初始化线程池
    6. /// @para num 线程数量
    7. void XThreadPool::Init(int num)
    8. {
    9. unique_lock lock(mux_);
    10. this->thread_num_ = num;
    11. cout << "Thread pool Init " << num << endl;
    12. }
    13. //
    14. /// 启动所有线程,必须先调用Init
    15. void XThreadPool::Start()
    16. {
    17. unique_lock lock(mux_);
    18. if (thread_num_ <= 0)
    19. {
    20. cerr << "Please Init XThreadPool" << endl;
    21. return;
    22. }
    23. if (!threads_.empty())
    24. {
    25. cerr << "Thread pool has start!" << endl;
    26. return;
    27. }
    28. //启动线程
    29. for (int i = 0; i < thread_num_; i++)
    30. {
    31. auto th = new thread(&XThreadPool::Run, this);
    32. threads_.push_back(th);
    33. }
    34. }
    35. //
    36. /// 线程池退出
    37. void XThreadPool::Stop()
    38. {
    39. is_exit_ = true;
    40. cv_.notify_all();
    41. for (auto& th : threads_)
    42. {
    43. th->join();
    44. }
    45. unique_lock lock(mux_);
    46. threads_.clear();
    47. }
    48. //线程池线程的入口函数
    49. void XThreadPool::Run()
    50. {
    51. cout << "begin XThreadPool Run " << this_thread::get_id() << endl;
    52. while (!is_exit())
    53. {
    54. auto task = GetTask();
    55. if (!task)continue;
    56. ++task_run_count_;
    57. try
    58. {
    59. task->Run();
    60. }
    61. catch (...)
    62. {
    63. }
    64. --task_run_count_;
    65. }
    66. cout << "end XThreadPool Run " << this_thread::get_id() << endl;
    67. }
    68. void XThreadPool::AddTask(XTask* task)
    69. {
    70. unique_lock lock(mux_);
    71. tasks_.push_back(task);
    72. task->is_exit = [this] {return is_exit(); };
    73. lock.unlock();
    74. cv_.notify_one();
    75. }
    76. XTask* XThreadPool::GetTask()
    77. {
    78. unique_lock lock(mux_);
    79. if (tasks_.empty())
    80. {
    81. cv_.wait(lock);
    82. }
    83. if (is_exit())
    84. return nullptr;
    85. if (tasks_.empty())
    86. return nullptr;
    87. auto task = tasks_.front();
    88. tasks_.pop_front();
    89. return task;
    90. }

    main.cpp 

    1. #include "xthread_pool.h"
    2. #include
    3. using namespace std;
    4. class MyTask :public XTask
    5. {
    6. public:
    7. int Run()
    8. {
    9. cout << "================================================" << endl;
    10. cout << this_thread::get_id()<<" Mytask " << name << endl;
    11. cout << "================================================" << endl;
    12. for (int i = 0; i < 10; i++)
    13. {
    14. if (is_exit())break;
    15. cout << "." << flush;
    16. this_thread::sleep_for(500ms);
    17. }
    18. return 0;
    19. }
    20. std::string name = "";
    21. };
    22. int main(int argc, char* argv[])
    23. {
    24. XThreadPool pool;
    25. pool.Init(16);
    26. pool.Start();
    27. MyTask task1;
    28. task1.name = "test name 001";
    29. pool.AddTask(&task1);
    30. MyTask task2;
    31. task2.name = "test name 002";
    32. pool.AddTask(&task2);
    33. this_thread::sleep_for(100ms);
    34. cout << "task run count = " << pool.task_run_count() << endl;
    35. this_thread::sleep_for(1s);
    36. pool.Stop();
    37. cout << "task run count = " << pool.task_run_count() << endl;
    38. getchar();
    39. return 0;
    40. }
    版本二(智能指针)

    xthread_pool.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. class XTask
    9. {
    10. public:
    11. virtual int Run() = 0;
    12. std::function<bool()> is_exit = nullptr;
    13. };
    14. class XThreadPool
    15. {
    16. public:
    17. //
    18. /// 初始化线程池
    19. /// @para num 线程数量
    20. void Init(int num);
    21. //
    22. /// 启动所有线程,必须先调用Init
    23. void Start();
    24. //
    25. /// 线程池退出
    26. void Stop();
    27. //void AddTask(XTask* task);
    28. void AddTask(std::shared_ptr task);
    29. std::shared_ptr GetTask();
    30. //线程池是否退出
    31. bool is_exit() { return is_exit_; }
    32. int task_run_count() { return task_run_count_; }
    33. private:
    34. //线程池线程的入口函数
    35. void Run() ;
    36. int thread_num_ = 0;//线程数量
    37. std::mutex mux_;
    38. //std::vector threads_;
    39. //线程列表 指针指针版本
    40. std::vector< std::shared_ptr > threads_;
    41. //std::list tasks_;
    42. std::list > tasks_;
    43. std::condition_variable cv_;
    44. bool is_exit_ = false; //线程池退出
    45. //正在运行的任务数量,线程安全
    46. std::atomic<int> task_run_count_ = {0};
    47. };

    xthread_pool.cpp 

    1. #include "xthread_pool.h"
    2. #include
    3. using namespace std;
    4. //
    5. /// 初始化线程池
    6. /// @para num 线程数量
    7. void XThreadPool::Init(int num)
    8. {
    9. unique_lock lock(mux_);
    10. this->thread_num_ = num;
    11. cout << "Thread pool Init " << num << endl;
    12. }
    13. //
    14. /// 启动所有线程,必须先调用Init
    15. void XThreadPool::Start()
    16. {
    17. unique_lock lock(mux_);
    18. if (thread_num_ <= 0)
    19. {
    20. cerr << "Please Init XThreadPool" << endl;
    21. return;
    22. }
    23. if (!threads_.empty())
    24. {
    25. cerr << "Thread pool has start!" << endl;
    26. return;
    27. }
    28. //启动线程
    29. for (int i = 0; i < thread_num_; i++)
    30. {
    31. //auto th = new thread(&XThreadPool::Run, this);
    32. auto th = make_shared(&XThreadPool::Run, this);
    33. threads_.push_back(th);
    34. }
    35. }
    36. //
    37. /// 线程池退出
    38. void XThreadPool::Stop()
    39. {
    40. is_exit_ = true;
    41. cv_.notify_all();
    42. for (auto& th : threads_)
    43. {
    44. th->join();
    45. }
    46. unique_lock lock(mux_);
    47. threads_.clear();
    48. }
    49. //线程池线程的入口函数
    50. void XThreadPool::Run()
    51. {
    52. cout << "begin XThreadPool Run " << this_thread::get_id() << endl;
    53. while (!is_exit())
    54. {
    55. auto task = GetTask();
    56. if (!task)continue;
    57. ++task_run_count_;
    58. try
    59. {
    60. task->Run();
    61. }
    62. catch (...)
    63. {
    64. }
    65. --task_run_count_;
    66. }
    67. cout << "end XThreadPool Run " << this_thread::get_id() << endl;
    68. }
    69. void XThreadPool::AddTask(std::shared_ptr task)
    70. {
    71. unique_lock lock(mux_);
    72. tasks_.push_back(task);
    73. task->is_exit = [this] {return is_exit(); };
    74. lock.unlock();
    75. cv_.notify_one();
    76. }
    77. std::shared_ptr XThreadPool::GetTask()
    78. {
    79. unique_lock lock(mux_);
    80. if (tasks_.empty())
    81. {
    82. cv_.wait(lock);
    83. }
    84. if (is_exit())
    85. return nullptr;
    86. if (tasks_.empty())
    87. return nullptr;
    88. auto task = tasks_.front();
    89. tasks_.pop_front();
    90. return task;
    91. }

     main.cpp

    1. #include "xthread_pool.h"
    2. #include
    3. using namespace std;
    4. class MyTask :public XTask
    5. {
    6. public:
    7. int Run()
    8. {
    9. cout << "================================================" << endl;
    10. cout << this_thread::get_id()<<" Mytask " << name << endl;
    11. cout << "================================================" << endl;
    12. for (int i = 0; i < 10; i++)
    13. {
    14. if (is_exit())break;
    15. cout << "." << flush;
    16. this_thread::sleep_for(500ms);
    17. }
    18. return 0;
    19. }
    20. std::string name = "";
    21. };
    22. int main(int argc, char* argv[])
    23. {
    24. XThreadPool pool;
    25. pool.Init(16);
    26. pool.Start();
    27. {
    28. auto task3 = make_shared();
    29. task3->name = "test shared 003";
    30. pool.AddTask(task3);
    31. auto task4 = make_shared();
    32. task4->name = "test shared 004";
    33. pool.AddTask(task4);
    34. }
    35. this_thread::sleep_for(100ms);
    36. cout << "task run count = " << pool.task_run_count() << endl;
    37. this_thread::sleep_for(1s);
    38. pool.Stop();
    39. cout << "task run count = " << pool.task_run_count() << endl;
    40. getchar();
    41. return 0;
    42. }

  • 相关阅读:
    软件设计模式系列之二十五——访问者模式
    Team Finance被黑分析|黑客自建Token“瞒天过海”,成功套取1450万美元
    考前必看科目一交警手势图解_准橙考试网
    详解mfc140.dll文件,修复mfc140.dll缺失的多种方法
    MySQL 触发器实例解析:自动更新垃圾桶颜色
    CocosCreator3.8研究笔记(十五)CocosCreator 资源管理Asset Bundle
    浏览器截图扩展增加快捷键
    你有用过 java中的栈和队列吗?怎么用栈来实现队列呢
    R语言矩阵数据初始化:创建全为0的矩阵、全为NA的矩阵、使用byrow参数指定数据排布方式(按行、按列)、dim参数指定矩阵维度信息
    【Python脚本进阶】2.2、构建一个SSH僵尸网络(下):利用SSH中的弱私钥
  • 原文地址:https://blog.csdn.net/weixin_42809675/article/details/132802041