文章目录
前言:
在多线程编程中,线程池是一种常用的技术,它可以有效地管理和调度线程。C++标准库中的和头文件提供了对线程池的支持。本文将介绍C++线程池的机制和原理。
一、线程池原理与实践
1、线程池的概念
线程池是一种线程使用模式,它通过预先创建多个线程,将任务提交给线程池,由线程池负责调度和执行任务。线程池的主要作用是减少线程创建和销毁的开销,提高程序的并发性能。
2、线程创建和销毁的开销有哪些?
线程创建与销毁的开销主要体现在以下几个方面:
2.1、系统资源消耗
- 内存分配:线程的创建需要为其分配一定的内存空间,以存储线程栈、线程控制块等数据结构。这些内存空间的分配和回收都需要消耗系统资源。
- 内核对象:在操作系统层面,线程的创建会涉及到内核对象的创建,这些对象用于管理线程的状态、优先级、调度等信息。同样地,线程的销毁也需要销毁这些内核对象。
2.2、初始化与清理
- 初始化:线程的创建需要进行一系列的初始化操作,如设置线程栈、初始化线程控制块等。这些操作都需要消耗一定的计算资源。
- 清理:线程的销毁需要执行一系列的清理操作,如释放线程栈、销毁线程控制块等。这些操作同样需要消耗计算资源。
2.3、上下文切换
- 当一个线程被创建或销毁时,操作系统需要进行上下文切换。上下文切换涉及到保存当前线程的上下文(如寄存器值、栈信息等)和加载新线程的上下文。这个过程需要消耗一定的CPU时间。
2.4、性能影响
- 创建开销:线程的创建通常比进程的创建要快,但在某些操作系统上,线程的创建仍然是一个相对昂贵的操作。例如,在Solaris 2操作系统上,创建进程的时间大约是创建线程的30倍。
- 销毁开销:线程的销毁同样需要一定的开销,特别是当线程被频繁创建和销毁时,这种开销会更加明显。
2.5、线程池的优势
- 减少开销:线程池通过预先创建一定数量的线程,并在需要时复用这些线程来执行任务,从而避免了频繁创建和销毁线程的开销。
- 提高性能:线程池通过限制并发线程的数量和有效地管理线程资源,可以提高系统的性能和资源利用率。
总结
线程创建与销毁的开销主要包括系统资源消耗、初始化与清理、上下文切换以及性能影响等方面。这些开销可以通过使用线程池等技术来降低,从而提高系统的整体性能和资源利用率。
3、线程池的机制
C++线程池主要由以下几个部分组成:
- 任务队列:用于存放待执行的任务。这些任务通常以函数对象、函数指针或可调用对象的形式存在。
- 线程集合:包含了一定数量的工作线程,这些线程会不断地从任务队列中取出任务并执行。
- 同步机制:用于协调线程之间的任务分配和状态同步。常见的同步机制包括互斥锁(mutex)、条件变量(condition variable)等。
- 线程管理:包括线程的创建、销毁、启动和停止等管理操作。为了优化性能,线程池通常会限制线程的最大数量,避免过多的线程创建和销毁开销。
cpp
#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__
#include <atomic>
#include <condition_variable>
#include <future>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include <functional>
class NoneCopy {
public:
~NoneCopy(){}
protected:
NoneCopy(){}
private:
NoneCopy(const NoneCopy&) = delete;
NoneCopy& operator=(const NoneCopy&) = delete;
};
class ThreadPool : public NoneCopy {
public:
//继承基类NoneCopy就不需要写如下删除了
//ThreadPool(const ThreadPool&) = delete;
//ThreadPool& operator=(const ThreadPool&) = delete;
static ThreadPool& instance() {
static ThreadPool ins;
return ins;
}
using Task = std::packaged_task<void()>;
~ThreadPool() {
stop();
}
template <class F, class... Args>
auto commit(F&& f, Args&&... args) ->
std::future<decltype(std::forward<F>(f)(std::forward<Args>(args)...))> {
using RetType = decltype(std::forward<F>(f)(std::forward<Args>(args)...));
if (stop_.load())
return std::future<RetType>{};
auto task = std::make_shared<std::packaged_task<RetType()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<RetType> ret = task->get_future();
{
std::lock_guard<std::mutex> cv_mt(cv_mt_);
tasks_.emplace([task] { (*task)(); });
}
cv_lock_.notify_one();
return ret;
}
int idleThreadCount() {
return thread_num_;
}
private:
ThreadPool(unsigned int num = std::thread::hardware_concurrency())
: stop_(false) {
{
/*
if (num <= 1)
thread_num_ = 2;
else
thread_num_ = num;
*/
thread_num_ = 10;
}
start();
}
void start() {
for (int i = 0; i < thread_num_; ++i) {
pool_.emplace_back([this]() {
while (!this->stop_.load()) {
Task task;
{
std::unique_lock<std::mutex> cv_mt(cv_mt_);
this->cv_lock_.wait(cv_mt, [this] {
return this->stop_.load() || !this->tasks_.empty();
});
if (this->tasks_.empty())
return;
task = std::move(this->tasks_.front());
this->tasks_.pop();
}
this->thread_num_--;
task();
this->thread_num_++;
}
});
}
}
void stop() {
stop_.store(true);
cv_lock_.notify_all();
for (auto& td : pool_) {
if (td.joinable()) {
std::cout << "join thread " << td.get_id() << std::endl;
td.join();
}
}
}
private:
std::mutex cv_mt_;
std::condition_variable cv_lock_;
std::atomic_bool stop_;
std::atomic_int thread_num_;
std::queue<Task> tasks_;
std::vector<std::thread> pool_;
};
#endif // !__THREAD_POOL_H__