线程的创建和销毁需要消耗操作系统的资源,如果频繁创建和销毁线程,会产生大量额外开销,甚至可能成为性能瓶颈。线程池中的核心线程通常是预先创建并存活的,当任务到达时,无需等待线程创建即可立即执行,减少了任务的等待时间,提升了系统的响应性。
目录
3 void enqueue(F &&f,Args&&... args)
1 原代码
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <functional>
class ThreadPoll
{
private:
std::vector<std::thread>theads;//线程数组
std::queue <std::function<void()>> tasks;//任务队列
std::mutex mutex1;//创建互斥锁
std::condition_variable condition;//条件变量,用于通知线程
bool is_stop;//线程池终止
public:
ThreadPoll(int numthread);
~ThreadPoll();
//入队函数,F函数,Args:参数
template<class F,class... Args>
void enqueue(F &&f,Args&&... args)
{
//传入线程函数
std::function<void()> task =
std::bind(std::forward<F>(f),std::forward<Args>(args)...);
{
//将函数加入tasks队列
std::unique_lock<std::mutex> lock(mutex1);
tasks.emplace(std::move(task));//入队
}
condition.notify_one();//通知一下
}
};
ThreadPoll::ThreadPoll(int numthread):is_stop(false)
{
for (int i = 0;i < numthread;i++)
{
theads.emplace_back([this]()
{
while(1)
{
std::unique_lock<std::mutex> lock(mutex1);
//非阻塞条件:1.任务为非空,2.线程终止
condition.wait(lock,[this](){return !tasks.empty() || is_stop;});
//1.进程要结束了-->触发stop,2.所有线程都已入队且结束-->都成立--->线程可终止
if (is_stop && tasks.empty()) return;
//取任务
std::function<void()> task(std::move(tasks.front()));
tasks.pop();
lock.unlock();//启动任务前解锁
task();
}
});
}
}
ThreadPoll::~ThreadPoll()
{
{
std::unique_lock<std::mutex> lock(mutex1);
is_stop = true;
}
//通知所有任务可以结束了
condition.notify_all();//通知
for (auto &thread: theads)
thread.join();
}
int main(int argc,char** argv)
{
ThreadPoll poll(4);
std::mutex cout_mutex;
for (int i = 0;i < 10;i++)
poll.enqueue([i,&cout_mutex](){
std::unique_lock<std::mutex> lock(cout_mutex);
std::cout << "线程" << i << "正在运行" << std::endl;
});
return 0;
}
std::vector<std::thread>theads;
创建一个存放线程的容器theads
std::queue <std::function<void()>> tasks;
创建一个队列容器,用以存放函数
std::mutex mutex1;
创建互斥锁,用以处理线程竞态
std::condition_variable condition;
创建条件变量,用以处理线程阻塞与唤醒
is_stop
解析信号,触发解析时,置1
2 ThreadPoll(int numthread)
ThreadPoll::ThreadPoll(int numthread):is_stop(false) { for (int i = 0;i < numthread;i++) { theads.emplace_back([this]() { while(1) { std::unique_lock<std::mutex> lock(mutex1); //非阻塞条件:1.任务为非空,2.线程终止 condition.wait(lock,[this](){return !tasks.empty() || is_stop;}); //1.进程要结束了-->触发stop,2.所有线程都已入队且结束-->都成立--->线程可终止 if (is_stop && tasks.empty()) return; //取任务 std::function<void()> task(std::move(tasks.front())); tasks.pop(); lock.unlock();//启动任务前解锁 task(); } }); } }在构造函数中,创建出numthread个线程,每个线程执行lambda表达式。
在lambda表达式中,如果任务队列tasks不为空,或者析构信号is_stop为真,则条件变量condition不阻塞。阻塞时解锁。
队列为空,且析构信号为真,则停止线程。
通过move()移动构造,转移函数所有权,避免额外的内存开销。
在线程执行出队之前,上锁保护线程,避免其他线程从任务重复获队列函数造成冲突。
出队并解锁,执行任务函数。
3 void enqueue(F &&f,Args&&... args)
template<class F,class... Args> void enqueue(F &&f,Args&&... args) { //传入线程函数 std::function<void()> task = std::bind(std::forward<F>(f),std::forward<Args>(args)...); { //将函数加入tasks队列 std::unique_lock<std::mutex> lock(mutex1); tasks.emplace(std::move(task));//入队 } condition.notify_one();//通知一下 }通过 万能引用&& 传入任意类型的可变参数据Args,以完美转发forward()保留参数原本的性质避免在万能引用的过程中参数发生语义转化。
将自定义的函数f与参数args传入到队列tasks中,notify_one()通知一个等待中的线程解除阻塞
4 ~ThreadPoll()
ThreadPoll::~ThreadPoll() { { std::unique_lock<std::mutex> lock(mutex1); is_stop = true; } //通知所有任务可以结束了 condition.notify_all();//通知 for (auto &thread: theads) thread.join(); }将析构信号is_stop置1,notify_all()唤醒所有阻塞中的线程,等待线程停止。
收到信号后,线程不再阻塞,无任务时停止。
int main(int argc,char** argv) { ThreadPoll poll(4); std::mutex cout_mutex; for (int i = 0;i < 10;i++) poll.enqueue([i,&cout_mutex](){ std::unique_lock<std::mutex> lock(cout_mutex); std::cout << "线程" << i << "正在运行" << std::endl; }); return 0; }在这段代码中,创建10个任务被放入队列中,被取出读取执行,最多同时4个线程运行,主函数触发return 0后触发析构,等待线程结束后,退出。
至此,线程池结构完成。如果这篇文章对你有帮助的话,请点个赞吧。

725

被折叠的 条评论
为什么被折叠?



