C++多线程的用法(包含线程池小项目)
一些小tips:
编译命令如下:
g++ 7.thread_pool.cpp -lpthread
查看运行时间:
time ./a.out
获得本进程的进程id:
this_thread::get_id()
需要引入的库函数有:
#include<thread> // 引入线程库
#include<mutex> // 加入锁机制需要引入库函数mutex
#include<condition_variable> // 引入信号量机制
定义信号量、锁:
condition_variable m_cond
std::mutex m_mutex;
所谓的多线程只不过就是指定的某一个函数为入口函数,的另外一套执行流程。
什么是临界资源?多线程情况下,大家都能访问到的资源。
进程是资源分配的最基本单位,线程是进程中的概念。
线程也是操作系统分配的一批资源。一个线程的栈所占用的空间有多大?——8M。查看命令:
ulimit -a
简单用法:
#include<iostream>
#include<thread>
using namespace std;#define BEGINS(x) namespace x{
#define ENDS(x) }BEGINS(thread_usage)
void func() {cout << "hello wolrd" << endl;return ;
}
int main() {thread t1(func); // t1已经开始运行了t1.join(); // 等待t1线程结束return 0;
}
ENDS(thread_usage)int main() {thread_usage::main();return 0;
}
那么如何给入口函数传参呢? 在C++中就很简单:
void print(int a, int b) {cout << a << " " << b << endl;return ;
}
int main() {thread t2(print, 3, 4);t2.join();return 0;
}
多线程封装思维:在相应的功能函数内部,不要去访问全局变量,类似于一个原子操作的功能。本身就支持多线程并行。
多线程程序设计:线程功能函数和入口函数要分开。(高内聚低耦合)
能不用锁就不用锁,因为这个锁机制会给程序运行效率带来极大的影响。
实现素数统计的功能
不加锁版:
#include<iostream>
#include<thread>
#include<cmath>
#include<mutex>
using namespace std;#define BEGINS(x) namespace x{
#define ENDS(x) }BEGINS(is_prime)
bool is_prime(int x) {for (int i = 2, I = sqrt(x); i <= I; i++) {if (x % i == 0) return false;}return true;
}
// 多线程——功能函数
int prime_count(int l, int r) {// 从l到r范围内素数的数量int ans = 0;for (int i = l; i <= r; i++) {ans += is_prime(i);}return ans;
}
// 多线程——入口函数
void worker(int l, int r, int &ret) {cout << this_thread::get_id() << "begin" << endl;ret = prime_count(l, r);cout << this_thread::get_id() << "dnoe" << endl;
}
int main() {#define batch 500000thread *t[10];int ret[10];for (int i = 0, j = 1; i < 10; i++, j += batch) {t[i] = new thread(worker, j, j + batch - 1, ref(ret[i]));}for (auto x : t) x->join();int ans = 0;for (auto x : ret) ans += x;for (auto x : t) delete x;cout << ans << endl;#undef batchreturn 0;
}
ENDS(is_prime)int main() {// thread_usage::main();is_prime::main();return 0;
}
加锁版:
#include<iostream>
#include<thread>
#include<cmath>
#include<mutex>
using namespace std;#define BEGINS(x) namespace x{
#define ENDS(x) }
BEGINS(prime_count2)
int ans = 0;
std::mutex m_mutex;
bool is_prime(int x) {for (int i = 2, I = sqrt(x); i <= I; i++) {if (x % i == 0) return false;}return true;
}
void prime_count(int l, int r) {cout << this_thread::get_id() << " begin\n";for (int i = l; i <= r; i++) {std::unique_lock<std::mutex> lock(m_mutex); // 临界区ans += is_prime(i);lock.unlock(); }cout << this_thread::get_id() << " done\n";return ;
}
int main() {#define batch 500000thread *t[10];for (int i = 0, j = 1; i < 10; i++, j += batch) {t[i] = new thread(prime_count, j, j + batch - 1);}for (auto x : t) x->join();for (auto x : t) delete x;cout << ans << endl;#undef batchreturn 0;
}
ENDS(prime_count2)int main() {prime_count2::main();return 0;
}
为什么不用++而是用+=
因为后者是原子操作,而前者不是,在多线程情况下可能存在覆盖写的问题。
__sync_fetch_and_add
这个函数也是原子操作。
#include<iostream>
#include<thread>
#include<cmath>
#include<mutex>
using namespace std;#define BEGINS(x) namespace x{
#define ENDS(x) }BEGINS(thread3)
int ans = 0;
bool is_prime(int x) {for (int i = 2, I = sqrt(x); i <= I; i++) {if (x % i == 0) return false;}return true;
}
void prime_count(int l, int r) {cout << this_thread::get_id() << "begin\n";for (int i = l; i <= r; i++) {int ret = is_prime(i);__sync_fetch_and_add(&ans, ret);}cout << this_thread::get_id() << "done\n";
}
int main() {#define batch 500000thread *t[10];for (int i = 0, j = 1; i < 10; i++, j += batch) {t[i] = new thread(prime_count, j, j + batch - 1);}for (auto x : t) x->join();for (auto x : t) delete x;cout << ans << endl;#undef batch return 0;
}
ENDS(thread3)int main() {thread3::main();return 0;
}
根据你提供的输出结果 `./a.out 2.06s user 0.02s system 385% cpu 0.538 total`,可以对各项含义进行解释:
- - `2.06s user`:表示程序在用户态消耗的CPU时间为2.06秒。这是程序执行期间用于执行用户代码的时间。
- - `0.02s system`:表示程序在内核态消耗的CPU时间为0.02秒。这是程序执行期间用于执行内核代码的时间。
- - `385% cpu`:表示程序使用了385%的CPU资源。这个值超过100%是因为程序可能在多个CPU核心上并行执行。所以 总时间 < 用户态+内核态时间 是非常正常的。
- - `0.538 total`:表示程序从开始执行到结束的总时间为0.538秒。这是包括了实际执行时间、等待时间和其他非CPU消耗的时间。
综上所述,这个输出结果告诉你程序的执行时间情况。用户态和内核态的CPU时间可以帮助你了解程序在不同执行环境中的资源消耗情况。CPU使用率告诉你程序在执行期间使用了多少CPU资源。总时间则是程序的整体执行时间。
线程池的实现
我们希望申请内存的动作是可控的。线程作为一种内存资源,在通常的设计模式下,申请线程资源并不可控。
解决办法就是线程池。在线程池内部,线程数量是可控的。把计算任务打包,扔到一个任务队列中。
什么是计算任务?——分为过程(函数方法)和数据(函数参数),如何进行打包——bind()方法。
线程池解决了传统的多线程程序设计中,面对不同任务我们需要实现不同程序的麻烦:而是直接往里塞任务就行了。这样就实现了资源的有效利用和管控。
m_cond.notify_one() 对于这个进程来说,是否同时也解锁了互斥资源?
是的,m_cond.notify_one()会解锁互斥资源。在调用m_cond.notify_one()之前,通常会先调用m_mutex.lock()来锁定互斥资源,然后在适当的时候调用m_cond.notify_one()来唤醒等待该条件的线程。在唤醒线程后,该线程会重新尝试获取互斥资源的锁,而其他等待的线程会继续等待。所以,m_cond.notify_one()不仅唤醒等待的线程,还会解锁互斥资源,使得等待的线程有机会获取互斥资源的锁。
子进程的中wait函数对互斥量进行解锁,同时线程进入阻塞或者等待状态。
/*************************************************************************> File Name: threadpool.cpp> Author: jby> Mail: > Created Time: Wed 13 Sep 2023 08:48:23 AM CST************************************************************************/#include<iostream>
#include<cmath>
#include<vector>
#include<unordered_map>
#include<queue>
#include<mutex>
#include<thread>
#include<condition_variable>
#include<functional>
using namespace std;#define BEGINS(x) namespace x {
#define ENDS(x) }BEGINS(thread_pool_test)
class Task {
public:template<typename FUNC_T, typename ...ARGS>Task(FUNC_T func, ARGS ...args) {this->func = bind(func, forward<ARGS>(args)...);}void run() {func();return ;}
private:function<void()> func; // 任意函数
};
class ThreadPool {
public:ThreadPool(int n = 1) : thread_size(n), threads(n), starting(false) {this->start();return ;}void worker() {auto id = this_thread::get_id(); // 获得本进程号running[id] = true;while (running[id]) {// 取任务Task *t = get_task(); t->run();delete t;}return ;}void start() {if (starting == true) return ; // 如果已经开始了就不用启动了for (int i = 0; i < thread_size; i++) {threads[i] = new thread(&ThreadPool::worker, this);}starting = true;return ;}template<typename FUNC_T, typename ...ARGS>void add_task(FUNC_T func, ARGS ...args) {unique_lock<mutex> lock(m_mutex); tasks.push(new Task(func, forward<ARGS>(args)...)); // 任务池相当于临界资源m_cond.notify_one(); // 生产者消费者模型return ;}void stop() {if (starting == false) return ; // 如果已经关了就不用再关了for (int i = 0; i < threads.size(); i++) { // 往队列末尾投递毒药任务this->add_task(&ThreadPool::stop_runnnig, this);}for (int i = 0; i < threads.size(); i++) {threads[i]->join();}for (int i = 0; i < threads.size(); i++) {delete threads[i]; // 释放那片进程剩余的空间threads[i] = nullptr; // 进程指针指向空}starting = false;return ;}~ThreadPool() {this->stop();while (!tasks.empty()) { // 如果任务队列里还有任务没执行完,全部丢弃delete tasks.front();tasks.pop();}return ;}
private:bool starting;int thread_size;Task *get_task() {unique_lock<mutex> lock(m_mutex);while (tasks.empty()) m_cond.wait(lock); // 生产者消费者模型 //子进程的中wait函数对互斥量进行解锁,同时线程进入阻塞或者等待状态。Task *t = tasks.front();tasks.pop();return t;}std::mutex m_mutex;std::condition_variable m_cond;vector<thread *> threads; // 线程池子unordered_map<decltype(this_thread::get_id()), bool> running; // 进程号到运行状态的哈希映射queue<Task *> tasks; // 任务队列void stop_runnnig() { // 毒药任务auto id = this_thread::get_id();running[id] = false;return ;}
};
bool is_prime(int x) {for (int i = 2, I = sqrt(x); i <= I; i++) {if (x % i == 0) return false;}return true;
}
int prime_count(int l, int r) {int ans = 0;for (int i = l; i <= r; i++) {ans += is_prime(i);}return ans;
}
void worker(int l, int r, int &ret) {cout << this_thread::get_id() << "begin\n";ret = prime_count(l, r);cout << this_thread::get_id() << "done\n";return ;
}
int main() {#define batch 500000ThreadPool tp(5); // 五个任务的窗口队列int ret[10];for (int i = 0, j = 1; i < 10; i++, j += batch) {tp.add_task(worker, j, j + batch - 1, ref(ret[i]));}tp.stop();int ans = 0;for (auto x : ret) ans += x;cout << ans << endl;#undef batchreturn 0;
}
ENDS(thread_pool_test)int main() {thread_pool_test::main();return 0;
}
相关文章:
C++多线程的用法(包含线程池小项目)
一些小tips: 编译命令如下: g 7.thread_pool.cpp -lpthread 查看运行时间: time ./a.out 获得本进程的进程id: this_thread::get_id() 需要引入的库函数有: #include<thread> // 引入线程库 #include<mutex> //…...
react ant ice3 实现点击一级菜单自动打开它下面最深的第一个子菜单
1.问题 默认的如果没有你的菜单结构是这样的: [{children: [{name: "通用配置"parentId: "1744857774620672"path: "basic"}],name: "系统管理"parentId: "-1"path: "system"} ]可以看到每层菜单的p…...
关于 Qt串口不同电脑出现不同串口号打开失败 的解决方法
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/132842297 红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…...
可观测性在灰度发布中的应用
前言 随着云计算的发展、云原生时代的来临,企业数字化转型进程不断深入,应用开发也越来越多地基于微服务化模式,快速迭代的能力使得应用开发更高效、更灵活。同时,也不得不面临应用版本快速升级所带来的的巨大挑战。 传统的发布方…...
vscode开发油猴插件环境配置指南
文章目录 一、环境配置1.1油猴插件开始编写代码1.2油猴插件配置1.2.1浏览器插件权限1.2.2插件自身权限 2. 油猴脚本API学习2.1 头文件2.2 油猴API 一、环境配置 1.1油猴插件开始编写代码 在vscode 中写入如下代码‘ // UserScript // name cds_test // namespace …...
网站不收录没排名降权怎么处理-紧急措施可恢复网站
网站降权对于SEO人员来说是非常致命的打击,因为网站一旦被搜索引擎降权,排名会严重地下降,网站的流量也会大幅下降,直接影响到收益。而且处理不好的话会导致恢复的时间周期无限拉长,所以网站被降权后我们要第一时间采取…...
C++vector模拟实现
vector模拟实现 1.构造函数2.拷贝构造3.析构赋值运算符重载4.iterator5.modifiers5.1push_back5.2pop_back5.3empty5.4insert5.5erase5.6swap 6.Capacity6.1size6.2capacity6.3reserve6.4resize6.5empty 7.Element access7.1operator[]7.2at 8.在谈reserve vector官方库实现的是…...
《DATASET DISTILLATION》
这篇文章提出了数据浓缩的办法,在前面已有的知识浓缩(压缩模型)的经验上,提出了不压缩模型,转而压缩数据集的办法,在压缩数据集上训练模型得到的效果尽可能地接近原始数据集的效果。 摘要 模型蒸馏的目的是…...
GDPU 数据结构 天码行空1
1. 病历信息管理 实现病历查询功能。具体要求如下: 定义一个结构体描述病人病历信息(病历号,姓名,症状);完成功能如下: 输入功能:输入5个病人的信息; 查询功能:输入姓名,在5个病历中进行查找,如果找到则显示该人的信息,…...
【C++】红黑树的模拟实现
🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风…...
【多线程】Thread 类 详解
Thread 类 详解 一. 创建线程1. 继承 Thread 类2. 实现 Runnable 接口3. 其他变形4. 多线程的优势-增加运行速度 二. Thread 类1. 构造方法2. 常见属性3. 启动线程-start()4. 中断线程-interrupt()5. 线程等待-join()6. 线程休眠-sleep()7. 获取当前线程引用 三. 线程的状态1. …...
LINUX 网络管理
目录 一、NetworkManager的特点 二、配置网络 1、使用ip命令临时配置 1)查看网卡在网络层的配置信息 2)查看网卡在数据链路层的配置信息 3)添加或者删除临时的网卡 4)禁用和启动指定网卡 2、修改配置文件 3、nmcli命令行…...
refresh rate
1920 x 1080 显卡刷新率 60...
使用 NGINX Unit 实施应用隔离
原文作者:Artem Konev - Senior Technical Writer 原文链接:使用 NGINX Unit 实施应用隔离 转载来源:NGINX 中文官网 NGINX 唯一中文官方社区 ,尽在 nginx.org.cn NGINX Unit 特性集的最新动态之一是支持应用隔离,该特…...
2023/09/12 qtc++
实现一个图形类(Shape) ,包含受保护成员属性:周长、面积, 公共成员函数:特殊成员函数书写 定义一个圆形类(Circle) ,继承自图形类,包含私有属性:半径 公共成员函数:特殊成员函数…...
全科医学科常用评估量表汇总,建议收藏!
根据全科医学科医生的量表使用情况,笔者整理了10个常用的全科医学科量表,可在线评测直接出结果,可转发使用,可生成二维码使用,可创建项目进行数据管理,有需要的小伙伴赶紧收藏! 日常生活能力量表…...
了解消息中间件的基础知识
为什么要使用消息中间件? 解耦:消息中间件可以使不同的应用程序通过解耦的方式进行通信,减少系统间的依赖关系提供异步通信:消息中间件可以实现异步消息传递,提高系统的响应性能。流量削峰:消息中间件可以…...
【linux】Linux wps字体缺失、加粗乱码解决
解决wps字体缺失问题 1、下载字体包 git clone https://github.com/iamdh4/ttf-wps-fonts.git2、创建单独放置字体的目录 mkdir /usr/share/fonts/wps-fonts3、复制字体到系统目录下 cp ttf-wps-fonts/* /usr/share/fonts/wps-fonts4、修改字体权限 chmod 644 /usr/share/f…...
每日两题 103二叉树的锯齿形层序遍历(数组) 513找树左下角的值(队列)
103 题目 103 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 示例 1: 输入:root [3,9,…...
ROS2报错:ImportError: cannot import name ‘Log‘ from ‘rosgraph_msgs.msg‘
在使用ros2的bag命令查看数据集信息时报错 Traceback (most recent call last):File "/opt/ros/noetic/bin/rosbag", line 34, in <module>import rosbagFile "/opt/ros/noetic/lib/python3/dist-packages/rosbag/__init__.py", line 33, in <mo…...
【Vue】Vue中的代码分为哪几种类型?
在 Vue 中的代码可以分为以下几种类型: 1.模板代码 模板代码是 Vue 中用来生成 HTML 的一种语法,可以通过 Vue 的模板语法和指令来动态渲染页面。模板代码一般写在 Vue 组件的 template 标签中。 2.JavaScript 代码 JavaScript 代码是 Vue 组件中用来…...
es6中includes用法
js中的includes用法 1.数组 includes 可以判断一个数组中是否包含某一个元素,并返回true 或者false [a,b,c].includes(a) true [a,b,c].includes(1) false includes可以包含两个参数,第二个参数表示判断的起始位置 起始位置第一个数字是0。 2.字符串 …...
QT中QRadioButton实现分组C++
通过对QRadioButton组件进行分组可解决QRadioButton组件的互斥性 实现如下。 假设已设计好UI并且有UI代码情况: 头文件引用: #include <QButtonGroup> 分组功能 ,cpp文件代码实现: Your_Project::Your_Project(QWidge…...
kafka实战报错解决问题
需求 在一个在线商城中,用户下单后需要进行订单的处理。为了提高订单处理的效率和可靠性,我们使用Kafka来实现订单消息的异步处理。当用户下单后,订单信息会被发送到Kafka的一个Topic中,然后订单处理系统会从该Topic中消费订单消…...
vite+react 使用 react-activation 实现缓存页面
对应的版本 "react": "^18.2.0", "react-activation": "^0.12.4", "react-dom": "^18.2.0", "react-router-dom": "^6.15.0",react-activation 这是一个npm包,在react keep alive…...
【android 蓝牙开发——蓝牙耳机】
【android 蓝牙开发——传统蓝牙】 【android 蓝牙开发——BLE(低功耗)蓝牙 2021-10-09更新】 总结一下蓝牙开发的基本使用以及蓝牙耳机的断开和链接。 所以需权限: <uses-permission android:name"android.permission.ACCESS_FIN…...
Golang goroutine 进程、线程、并发、并行
goroutine 看一个需求 需求:要求统计1-200000000000的数字中,哪些是素数? 分析思路: 1)传统的方法,就是使用一个循环,循环的判断各个数是不是素数(一个任务就分配给一个cpu去做,这样很不划算…...
如何做到安全上网
随着信息化的发展,企业日常办公越来越依赖互联网,而访问互联网过程中,会遇到各种各样不容忽视的风险,例如员工主动故意的数据泄漏,后台应用程序偷偷向外部发信息,木马间谍软件的外联,以及各种挖…...
优维低代码实践:菜单
优维低代码技术专栏,是一个全新的、技术为主的专栏,由优维技术委员会成员执笔,基于优维7年低代码技术研发及运维成果,主要介绍低代码相关的技术原理及架构逻辑,目的是给广大运维人提供一个技术交流与学习的平台。 优维…...
git merge 如何撤销
如果只是 git merge 未进行其他 git 操作,可以使用 git merge --abort 撤销如果 git merge 之后,再 git add,可以使用 git reset HEAD 或 git reset HEAD file (前者多个文件,后者单个文件)如果 git merge 之后,再 git…...
珠海网站策划公司/最新的域名网站
python十六进制字符串To assign a hexadecimal value in the string so that it can be printed as a string, we use \x that is known as Escape sequence”, it represents that given value is the hexadecimal value. 为了在字符串中分配一个十六进制值以便可以将其打印为…...
做mv主题网站/重庆百度seo公司
https://zhuanlan.zhihu.com/p/29150809 一、数据库有锁机制的原因。 数据库锁定机制简单来说,就是数据库为了保证数据的一致性和有效性,而使各种共享资源在被并发访问变得有序所设计的一种规则。对于任何一种数据库来说都需要有相应的锁定机制ÿ…...
佛山小网站建设/店铺运营
前言 对于机器学习领域的开发人员来说,Python是最流行的编程语言之一。Python既不是最快的语言(很容易被C和C取代),也不一定是最容易学习的语言(R和Matlab可以有更小的学习曲线)。那么,为什么python被 57%的数据科学家和机器学习开发人员 排名…...
做设计太依赖网站素材/网络媒体发稿
概述在实际的业务场景应用中,我们经常要根据业务条件获取并筛选出我们的目标数据。这个过程我们称之为数据查询的过滤。而过滤过程使用的各种条件(比如日期时间、用户、状态)是我们获取精准数据的必要步骤,这样才能得到我们期望的结果。所以本章我们来学…...
十大采购平台/seo代理
MyBatis中mapper.xml中foreach的使用 Author:kak MySql的动态语句foreach,当传入参数为数组或者集合时需要通过foreach标签进行遍历,其主要是在in条件中,可以在SQL语句中迭代一个集合; 综述 <foreach collection&…...
企业网站怎么做seo优化/亚马逊跨境电商开店流程及费用
本片博文开始讲解SAP前台是如何实现ICS业务模式的。 一、VA01开立销售订单 我这里为了方便,创建了一个订单类型ZMIV作为公司间销售的订单类型,其实公司间销售订单跟标准的销售订单是一致的。同时,销售组织选的是接单公司的销售组织。 回车之后…...