C++多线程学习(十七、简单实现线程池)

目录

线程池

设计线程池的关键问题

代码

可能出现的疑问

queue> task;

总结:

template    auto InsertQueue(T&& t, Args&& ...args)->future;(t(args...))>

总结:

ThreadPool(size_t size);构造函数

总结:

templateauto ThreadPool::InsertQueue(T&& t, Args&& ...args)->future(t(args...))>

总结:


线程池

线程池是一种用于管理和控制线程的机制。它包含一个固定数量的线程组成的池子,线程池可以接收任务并分配线程来执行这些任务。线程池的主要目的是通过减少线程的创建和销毁次数,来提高线程的复用性和效率。

线程池的作用包括以下几个方面:
1. 提高性能:线程池可以避免频繁地创建和销毁线程,减少了系统开销,提高了响应速度和处理能力。
2. 提供线程管理和控制:线程池可以统一管理线程的创建、销毁和调度,可以根据任务的情况动态调整线程数目。
3. 避免资源竞争:线程池可以有效地避免多个线程同时访问共享资源导致的竞争问题,通过控制并发访问,提高了程序的稳定性和安全性。
4. 提供任务队列:线程池中的任务可以被放入任务队列中,按照先进先出的顺序执行,确保任务的顺序性和有序性。

设计线程池的关键问题

1.可使用的线程数量

2.高效的分配任务的方式

3.如何等待任务完成

 

代码

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<queue>
#include<functional>	//提供了函数对象和函数模板
#include<future>		//异步操作
#include<exception>		//异常用头文件
using namespace std;class ThreadPool
{
public:ThreadPool(size_t size);~ThreadPool();//函数模板template<class T,class ...Args>auto InsertQueue(T&& t, Args&& ...args)->future<decltype(t(args...))>;
protected:queue<function<void()>> task;	//任务列表vector<thread> workers;			//工作线程mutex mtx_queue;condition_variable cv;bool stop;
};
//测试函数
int func(int i)
{printf("线程-%d-进行工作\n", i);//this_thread::sleep_for(1s);printf("线程-%d-工作结束\n", i);return i * i;
}int main()
{ThreadPool pool(4);//创建线程池,4个线程vector<future<int>> result;for (int i = 0; i < 1000; i++){result.emplace_back(pool.InsertQueue(func, i));}for (auto&& v:result){printf("结果:%d\n", v.get());}return 0;
}
ThreadPool::ThreadPool(size_t size):stop(false)
{for (int i = 0; i < size; i++){workers.emplace_back([this]{while (true){function<void()> task;{unique_lock<mutex> lock(this->mtx_queue);this->cv.wait(lock, [this] {return this->stop || !this->task.empty(); });if (this->stop &&this->task.empty()){return;}task = move(this->task.front());this->task.pop();}task();}});}
}
ThreadPool::~ThreadPool()
{{unique_lock<mutex> lock(mtx_queue);stop = true;}cv.notify_all();for (auto &t :workers){t.join();}
}
template<class T, class ...Args>
auto ThreadPool::InsertQueue(T&& t, Args&& ...args)->future<decltype(t(args...))>
{using Type = decltype(t(args...));auto task = make_shared<packaged_task<Type()>>(bind(forward<T>(t), forward<Args>(args)...));future<Type> res = task->get_future();{unique_lock<mutex> lock(mtx_queue);if (stop){throw runtime_error("停止。。");}this->task.emplace([task] {(*task)();});}cv.notify_one();return res;
}

可能出现的疑问

queue<function<void()>> task;

这是一个队列(queue),其中存储了一系列的函数对象(function),这些函数对象中的函数类型为void(),即没有参数并且无返回值的函数

这样的设计通常用于实现任务调度或异步操作。

总结:

通过将需要执行的任务(函数)依次添加到队列中,可以实现任务的顺序执行,避免了函数之间的并发访问问题。调用者可以按照自己的需求,从队列中取出函数对象并进行执行,从而实现任务的调度和执行。

template<class T,class ...Args>
    auto InsertQueue(T&& t, Args&& ...args)->future<decltype(t(args...))>;

这是一个函数模板,用于将任务插入到队列中返回一个future对象。

它有两个模板参数T和Args

T是任务的类型,它可以是一个函数指针、函数对象或lambda表达式,表示要执行的任务。

Args是可变参数模板,表示任务执行所需的参数。

函数模板的返回类型是一个auto类型,通过decltype(t(args...))来推导:这意味着返回类型将根据传入的任务类型和参数类型而定。

该函数将接受一个任务t和一系列参数args,并将它们传递给任务t进行执行。

然后,它将返回一个future对象,用于获取任务执行的结果。



总结:

使用这个函数模板,可以方便地将任务插入到队列中,并在需要时获取任务的执行结果。

ThreadPool(size_t size);构造函数

这是一个线程池的实现。首先,在构造函数中初始化一个指定大小的线程池,并使用一个布尔变量 stop 作为停止标志。然后,通过循环创建指定数量的线程,每个线程都执行一个 lambda 表达式。

lambda 表达式是一个无限循环,它首先获取线程池的锁 mtx_queue,然后等待条件变量 cv 的通知,条件线程池停止任务队列为空。如果满足条件,线程将退出循环。否则,它会从任务队列中取出一个任务并执行。

总结:

整个过程中,线程池使用互斥锁 mtx_queue 来保证多个线程对任务队列的访问是线程安全的。同时,条件变量 cv 用于在任务队列为空或线程池停止时进行通知和等待。

template<class T, class ...Args>
auto ThreadPool::InsertQueue(T&& t, Args&& ...args)->future<decltype(t(args...))>

InsertQueue 函数的目的是将一个任务添加到线程池的任务队列中,并返回一个 future 对象,用于获取任务执行的结果。

该函数使用了可变参数模板完美转发,可以接受任意类型的任务和参数。在函数内部,它首先使用 decltype 推导出任务的返回类型,并创建一个 packaged_task 对象来封装任务。

接下来,函数获取任务的 future 对象,并在一个作用域内获取任务队列的互斥锁 mtx_queue。如果线程池已经停止,函数将抛出一个运行时异常。

然后,函数使用 emplace 方法将 lambda 表达式添加到任务队列中。这个 lambda 表达式实际上是通过 bind 将任务和参数绑定,并使用 (*task)() 执行任务

最后,函数通过条件变量 cv 发送通知,告知线程池有新的任务可执行。并返回之前获取的任务结果的 future 对象。

总结:

这段代码的作用是将一个任务添加到线程池的任务队列中,并返回一个 future 对象,以便在需要时获取任务的执行结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/18110.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 CSS v-bind 计算和三元运算

官方文档 中指出&#xff1a;CSS 中的 v-bind 支持 JavaScript 表达式&#xff0c;但需要用引号包裹起来&#xff1a; 例子如下&#xff1a; <script lang"ts" setup> const treeContentWidth ref(140); </script><style lang"less" scop…

springcloud eureka增加安全认证

网上很多资料写的不全&#xff0c;不细致。 springcloud架构&#xff0c;本地运行代码是eureka地址一般为localhost:port&#xff08;自己暴露的端口&#xff09;&#xff0c;例如http://localhost:9000/ &#xff0c;但是如果在服务器&#xff0c;且使用k8s部署&#xff0c;一…

MFC List Control控件添加单元格编辑和单元格下拉列表

文章目录 初始化编译栏的创建与销毁下拉列表的创建与销毁自定义编译栏与下拉框的焦点问题点击CListCtrl 事件处理程序双击添加全部代码 ui 设置 初始化 #define IDC_EDIT_CREATEID 3000 #define IDC_COMBOX_CREATEID 3001int e_Item0; //刚编辑的行 int e_SubItem2; //刚…

JavaWeb 速通JavaScript

目录 一、JavaScript快速入门 1.基本介绍 : 2.JavaScript特点 : 3.JavaScript使用方式 : 1 方式一 : 写在 2 方式二 : 以外部文件形式引入 PS : 注意事项 4.JavaScript查错方式 : 二、JavaScript数据类型 1.变量 : 2.数据类型 : 3.特殊值 : 三、JavaScript运算符 1.算…

对文件中的数据进行排序

#include<stdio.h> #include<stdlib.h> #include<time.h> int main()//生成1000个随机数据 {srand((size_t)time(NULL));FILE* fpfopen("d:/data.txt","w");if(!fp) return -1;for(int i0;i<1000;i)fprintf(fp,"%d\n",ran…

轻创数字人集团一文论述如何助力中小型企业数字化转型 ?

随着互联网技术的不断发展&#xff0c;人工智能已经成为了许多企业数字化转型的重要工具。然而&#xff0c;在人工智能领域中&#xff0c;不乏一些大型公司在研发和应用方面占据着主导地位。例如&#xff0c;以基础技术和软硬件设施为主的上游层&#xff0c;以英伟达、Meta、Ep…

x3550M5服务器,2008r2系统,关机后再开机,提示需要系统修复

问题现象&#xff1a; x3550M5服务器&#xff0c;2008r2系统&#xff0c;关机后再开机&#xff0c;提示需要修复&#xff0c;选择语言&#xff0c;点击下一步&#xff0c;选择操作系统一栏是空白的 &#xff08;加载前的图忘记拍&#xff09; 问题分析&#xff1a; 根据网上…

vue启动失败;‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。

问题1&#xff1a; 启动vue项目失败&#xff1a; vue启动失败&#xff1b;‘vue-cli-service‘ 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 获或者问题2&#xff1a;npm i 失败&#xff1a; C:\Users\28602\AppData\Roaming\npm-cache_logs\2023-07-07…

微服务学习1——微服务环境搭建

微服务学习1——微服务环境搭建 &#xff08;参考黑马程序员项目&#xff09; 个人仓库地址&#xff1a;https://gitee.com/jkangle/springboot-exercise.git 微服务就是将单体应用进一步拆分&#xff0c;拆成更小的服务&#xff0c;拆完之后怎么调用&#xff0c;主流的技术有…

spring boot+MySQL福聚苑社区团商品购系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9

R语言——字符串处理

paste(abc, def, gh, sep ) #粘贴字符串 substr(abcdefg, 2, 3) # 取特定字符串 gsub(abc, , c(abc, abcc, abcbc)) # 将字符串中abc替换为空 strsplit(a;b;c, ;, fixed T) # 按照;切分字符串 strsplit(a222b2.2c, 2.2, fixed F) # 按照正则表达式分隔&#xff0c;这里的.是…

RAID5更换磁盘操作导致数据丢失的数据恢复案例

服务器数据恢复环境&#xff1a; 曙光某型号光纤存储柜&#xff0c;16块光纤磁盘组建了2组RAID5磁盘阵列&#xff0c;每组raid5阵列中有7块成员盘&#xff0c;另外2块磁盘配置为全局热备盘使用。 第一组RAID5阵列划分了3个LUN&#xff1a;1个LUN分配给linux主机、第2个LUN分配给…