【C++ 并发 线程池】轻松掌握C++线程池:从底层原理到高级应用(一)

简介: 【C++ 并发 线程池】轻松掌握C++线程池:从底层原理到高级应用

一、线程池基本概念与原理

1.1 线程池概念及优势

C++线程池简介

线程池是一种并发编程技术,它能有效地管理并发的线程、减少资源占用和提高程序的性能。C++线程池通过库,结合C++ 11、14、17、20等的新特性,简化了多线程编程的实现。

提高性能与资源利用率

线程池主要解决两个问题:线程创建与销毁的开销以及线程竞争造成的性能瓶颈。通过预先创建一组线程并复用它们,线程池有效地降低了线程创建和销毁的时间和资源消耗。同时,通过管理线程并发数量,线程池有助于减少线程之间的竞争,增加资源利用率,并提高程序运行的性能。

线程创建开销解决

多线程环境下,每当需要执行一个任务时,创建与销毁线程都需要额外的系统资源。线程池通过预先创建一定数量的线程,可以减少这种资源消耗。例如:

方式 创建开销 销毁开销
无线程池 较高 较高
有线程池 很低 很低

线程竞争问题解决

过多的线程可能导致线程竞争,影响系统性能。线程池通过维护一个可控制的并发数量,有助于减轻线程之间的竞争。例如,当CPU密集型任务和I/O密集型任务共存时,可以通过调整线程池资源,实现更高效的负载平衡。

1.2 线程池工作原理

线程池通过预先创建和调度复用线程来实现资源优化。这个过程主要包括:创建线程、任务队列与调度、以及线程执行及回收。

创建线程

线程池在初始化时会预先创建一定数量的线程,这些线程将会被后续任务复用。线程的数量可以根据实际需求和系统资源进行配置。以下是一个创建线程的示例:

for (size_t i = 0; i < threadCount; ++i) {
    threads.emplace_back(threadFunc, this);
}

任务队列与调度

线程池通过维护一个任务队列来管理待执行任务。当线程池收到一个新任务时,它会将任务加入到任务队列中。线程会按照预定策略(例如FIFO)从队列中取出任务执行。以下是一个简单的任务队列操作示例:

void ThreadPool::addTask(const Task& task) {
    {
        lock_guard<mutex> lock(queueMutex);
        taskQueue.emplace(task);
    }
    condition.notify_one();
}

同时,线程池可能实现更复杂的调度策略,比如优先级调度、分组调度等。

线程执行及回收

线程执行任务时,会遵循线程池的调度策略从任务队列中获取任务。任务完成后,线程将被放回到线程池中等待下一个任务,而不是销毁。这种复用机制提高了资源利用率并降低了线程创建销毁的开销。以下是一个线程拿取任务及执行的例子:

void ThreadPool::threadFunc() {
    while (true) {
        Task task;
        {
            unique_lock<mutex> lock(queueMutex);
            condition.wait(lock, [this]() { return !taskQueue.empty() || terminate; });
            if (terminate && taskQueue.empty()) {
                break;
            }
            task = taskQueue.front();
            taskQueue.pop();
        }
        task(); // Execute the task.
    }
}

线程池的回收主要涉及任务完成通知、等待所有线程结束、资源回收与释放等方面,这部分内容将在后面的章节进行详细阐述。

1.3 C++线程池常用库与实现方法

C++线程池实现主要依赖于多线程库的支持,例如std::thread、Boost.Thread库和Poco C++库。下面我们将分别介绍这些库的基本概况和特点。

std::thread

std::thread是C++ 11提供的原生线程库,它简化了多线程编程,提供了线程创建、管理和同步等基本功能。使用std::thread构建线程池时,可以利用C++ 11/14/17/20的新特性,编写简洁高效的代码。但需要注意的是,std::thread库本身并不提供线程池实现,需要根据线程池的工作原理自行实现。

Boost.Thread库

Boost.Thread库是Boost C++库中的一个子库,提供了线程创建、管理和同步等功能。相较于std::thread,Boost.Thread库提供了更丰富的功能,例如线程属性、线程组管理等。虽然它在C++ 11之前就已经存在,但仍然与C++ 11/14/17/20的特性相兼容。使用Boost.Thread库构建线程池需要自行实现线程池的相关概念和结构。

Poco C++库

Poco C++库是一个跨平台的C++库,包含了许多模块,其中也包含线程及线程池模块。Poco的线程池实现已经封装好了线程池的基本功能,如创建线程、管理任务队列等。使用Poco库构建线程池相对于上述两个库更方便快捷,但在性能和灵活度上略有所损失。

为了实现更好的性能与灵活度,本博客主要采用std::thread作为基本库,并结合其他C++新特性实现线程池。后续章节将细致介绍线程池的底层实现以及高级应用及优化方法。

二、C++线程池底层实现详解

2.1 创建线程及初始化线程池

创建线程和初始化线程池需要处理如下几个方面:线程创建、线程池参数配置和任务队列初始化。

线程创建

线程创建使用std::thread库提供的功能。首先,定义一个线程执行函数,该函数为线程在运行时的执行体。在线程池类中,可以创建一个指定数量的线程集合,将线程执行函数作为参数传递给它们。以下是创建线程的代码示例:

class ThreadPool {
    // ...
private:
    void threadFunction(); // 线程执行函数的声明
    vector<thread> threads; // 线程集合
    // ...
};
ThreadPool::ThreadPool(size_t threadCount) {
    for (size_t i = 0; i < threadCount; ++i) {
        threads.emplace_back(&ThreadPool::threadFunction, this);
    }
}

其中,threadCount是设定的线程池线程数量。

线程池参数配置

线程池的参数配置可以如下所示:

  • 配置线程数量:根据硬件资源和任务性质预先创建一定数量的线程。线程数量的设置需要权衡效率和资源占用两方面因素。可参考:https://stackoverflow.com/questions/2332765/how-to-determine-the-optimal-thread-pool-size
  • 是否允许动态增减线程:根据任务数量和系统配置,动态调整线程池中的线程数量。
  • 自定义调度策略:为线程池指定任务调度策略,如优先级调度、FIFO等。

任务队列初始化

在线程池类中,维护一个任务队列用于管理待执行任务。可使用线程安全的容器(例如deque),配合互斥量(std::mutex)和条件变量(std::condition_variable)实现任务队列的同步访问。

class ThreadPool {
    // ...
private:
    // 任务队列相关
    deque<Task> taskQueue;             // 任务队列
    mutex queueMutex;                  // 任务队列访问互斥量
    condition_variable condition;      // 任务队列条件变量,通知线程有新任务可执行
    // ...
};

至此,线程池的创建和初始化部分已经完成。接下来的章节将深入讲解任务调度与执行、以及线程池的优雅终止。


【C++ 并发 线程池】轻松掌握C++线程池:从底层原理到高级应用(二)https://developer.aliyun.com/article/1464326

目录
相关文章
|
1月前
|
存储 算法 Java
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
74 0
|
4天前
|
设计模式 C语言 C++
【C++进阶(六)】STL大法--栈和队列深度剖析&优先级队列&适配器原理
【C++进阶(六)】STL大法--栈和队列深度剖析&优先级队列&适配器原理
|
4天前
|
存储 C++
C++底层原理
C++底层原理
13 0
|
1月前
|
安全 Java 调度
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
【C/C++ 线程池设计思路 】设计与实现支持优先级任务的C++线程池 简要介绍
45 2
|
1月前
|
Linux API C++
【C++ 线程包裹类设计】跨平台C++线程包装类:属性设置与平台差异的全面探讨
【C++ 线程包裹类设计】跨平台C++线程包装类:属性设置与平台差异的全面探讨
51 2
|
5天前
|
存储 编译器 C语言
c++的学习之路:5、类和对象(1)
c++的学习之路:5、类和对象(1)
21 0
|
5天前
|
C++
c++的学习之路:7、类和对象(3)
c++的学习之路:7、类和对象(3)
19 0
|
4天前
|
设计模式 Java C++
【C++高阶(八)】单例模式&特殊类的设计
【C++高阶(八)】单例模式&特殊类的设计
|
4天前
|
编译器 C++
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
【C++基础(八)】类和对象(下)--初始化列表,友元,匿名对象
|
8天前
|
存储 安全 C语言
【C++】string类
【C++】string类