线程池的简单实现及其核心参数
线程池简单实现
这里给出线程池实现的简易代码及其核心参数,面试时够用!!!
线程池基于生产消费者模型实现,生产者负责生产任务(考虑线程的同步),将其放在工作队列(任务队列)中,然后消费者在构造函数中按照一定的条件(涉及到线程的同步)将任务从任务队列中拿出来进行处理。最后在任务全部执行结束后,唤醒线程,在析构函数中释放线程资源。
其中包含了三个部分
构造函数:消费者,从任务队列中取出任务执行。如果标志变量stop_为false并且队列为空,则阻塞,注意示例代码中给出了两种条件变量wait的方法,条件刚好相反。
void addtask(std::function<void()> task); :生产者,将任务放到工作队列中。
析构函数:stop_置为true,唤醒所有线程,释放所有线程的资源。
注意,实例化传参时,使用绑定器std::bind传参非常方便,绑定器std::bind和包装器std::function可以有效替代C中的函数指针。
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <functional>
#include <atomic>
#include <Windows.h>
class ThreadPool
{
private:
//线程池的线程集合
std::vector<std::thread> threads_;
//任务队列,存储等待执行的任务
std::queue<std::function<void()>> taskqueue_;
//互斥锁,用于同步对任务队列的访问
std::mutex mutex_;
//条件变量,用于线程间的同步
std::condition_variable condition_;
//停止标志,用于通知线程退出
std::atomic_bool stop_;
public:
//构造函数,用于启动指定数量的线程
ThreadPool(size_t thread_num);
//将任务添加到任务队列中
void addtask(std::function<void()> task);
//析构函数,停止线程池中的所有线程
~ThreadPool();
};
ThreadPool::ThreadPool(size_t thread_num)
{
//启动thread_num个线程,每个线程阻塞在条件变量上
for (size_t ii = 0; ii < thread_num; ++ii)
{
threads_.emplace_back([this] {
while (!stop_)
{
std::function<void()> task;
{
//锁作用域的开始
std::unique_lock<std::mutex> lock(this->mutex_);
//等待生产者的条件变量
//while (!this->stop_ && taskqueue_.empty())
//{
// this->condition_.wait(lock);
//}
this->condition_.wait(lock, [this] {return stop_ || !taskqueue_.empty(); });
//在线程池停止之前,如果队列中还有任务,执行完再退出
if (stop_ && taskqueue_.empty())return;
//出队一个任务
task = std::move(this->taskqueue_.front());
this->taskqueue_.pop();
}
task();
}
});
}
}
void ThreadPool::addtask(std::function<void()> task)
{
{
//锁作用域的开始
std::lock_guard<std::mutex> lock(mutex_);
taskqueue_.push(task);
}
condition_.notify_one();
}
ThreadPool::~ThreadPool()
{
stop_ = true;
condition_.notify_all();//唤醒全部的线程
//等待全部线程执行完任务后退出
for (std::thread& th : threads_)
{
th.join();
}
}
void print1()
{
printf("I have a pen!\n");
}
void print2(int a)
{
printf("There are %d dogs.\n", a);
}
int main()
{
ThreadPool threadpool(3);
threadpool.addtask(std::bind(print1));
Sleep(1);
threadpool.addtask(std::bind([] {printf("I have a cat!\n"); }));
Sleep(1);
threadpool.addtask(std::bind(print2, 5));
Sleep(1);
return 0;
}
线程池的核心参数
核心线程数 线程池的基本大小,即使核心线程空闲,线程池也不会销毁。
最大线程数 线程池允许创建的最大线程数,当工作队列已满并且当前线程池线程池数小于最大线程池数时,线程池通过线程工厂创建新的线程执行任务。
线程存活时间 非核心线程闲置后存活的时间,超过该时间未被复用,非核心线程将被销毁。
时间单位 线程存活时间的时间单位。
工作/任务队列 存放提交但未执行的任务。
线程工厂 用于创建线程的线程工厂。
拒绝策略 当工作队列已满并且当前线程数已经等于最大线程池数,对任务的拒绝方式。
线程池的常见问题
首先声明一点,这里的常见问题主要针对面试题目,而且旨在简要,如果还想扩展,自行搜索其他内容,我这里主要给核心关键内容,很多内容会省略,面试时自行发散。
1.介绍线程池
线程池就是用来管理线程的池子,可以反复利用线程池中的线程。
2.线程池的优点
1.降低资源消耗。避免线程频繁创建和销毁造成的资源消耗。
2.统一管理线程,方便易用。
3.常见线程池
1.定长线程池(FixedThreadPool):只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。
2.定时线程池(ScheduledThreadPool):核心线程数量固定,非核心线程数量无限,执行完闲置10ms后回收,任务队列为延时阻塞队列。
3.可缓存线程池(CachedThreadPool):无核心线程,非核心线程数量无限。执行完闲置60s后回收,任务队列为不存储元素的阻塞队列。
4.单线程化线程池(SingleThreadExecutor):只有1个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列。