C++11 多线程编程-小白零基础到手撕线程池
提示:文章
文章目录
- 前言
- 一、背景
- 二、
- 2.1
- 2.2
- 总结
前言
前期疑问:
本文目标:
一、背景
来源于b站视频
C++11 多线程编程-小白零基础到手撕线程池
学习来源:https://www.bilibili.com/video/BV1d841117SH/?p=2&spm_id_from=pageDriver&vd_source=1a54eaaaa0e36b9ef70e2dbe59d5b137
http://www.seestudy.cn/?list_9/35.html
相关知识点
thread | 创建线程 | |
---|---|---|
join | ||
detach | ||
joinable | ||
std::ref | ||
智能指针 | 在头文件中 |
二 、互斥量
自己写的代码
2.1 互斥量
#include <iostream>
#include <thread>int counter = 0;void fun()
{for (int i = 0; i < 10000; i++){counter++;}
}int main() {std::thread t1(fun);std::thread t2(fun);t1.join();t2.join();std::cout << "a:" << counter << std::endl;
}
没有实现竞争访问a导致结果不是2000的情况,我这边的打印结果是20000。
看有的评论说加一个0实现了,我加了好几个0还是没有实现。
有的评论说现在编译器都是2000了,不明所以,继续向下看吧。
第二天来用了课程对应的代码重新跑了一下,同时循环次数尝试加了个0。又出现了变量打印的值不是20000的情况。而我的myFirstTest工程依然出现不了预期的情况,先不管了。
2.2 互斥量锁死
#include <iostream>
#include <thread>
#include <mutex>std::mutex m1, m2;int counter = 0;//死锁
void fun1()
{for (int i = 0; i < 5000; i++){m1.lock();m2.lock();m1.unlock();m2.unlock();}
}void fun2()
{for (int i = 0; i < 5000; i++){m2.lock();m1.lock();m1.unlock();m2.unlock();}
}void fun()
{for (int i = 0; i < 1000000; i++){counter++;}
}int main() {std::thread s1(fun1);std::thread s2(fun2);s1.join();s2.join();std::thread t1(fun);std::thread t2(fun);t1.join();t2.join();std::cout << "a:" << counter << std::endl;
}
循环次数50、500都不会锁死,5000会锁死。
视频中给出的防止死锁的解决办法是,每个线程都先获取m1,继续获取m2。顺序获取。可以防止死锁。
(2024年9月29日17:11:45 今天再看代码,理解之前写的【顺序获取。可以防止死锁。】,应该是修改上述代码为都先获取m1,再获取m2)
5、std::lock_guard
#include <iostream>
#include <thread>
#include <mutex>std::mutex tex;
std::mutex m1, m2;int counter = 0;void fun()
{for (int i = 0; i < 1000000; i++){std::lock_guard<std::mutex> lg(tex);counter++;}
}int main() {std::thread t1(fun);std::thread t2(fun);t1.join();t2.join();std::cout << "a:" << counter << std::endl;
}
lock_guard源码
template<class _Mutex>class lock_guard{ // class with destructor that unlocks a mutex
public:using mutex_type = _Mutex;explicit lock_guard(_Mutex& _Mtx): _MyMutex(_Mtx){ // construct and lock_MyMutex.lock();}lock_guard(_Mutex& _Mtx, adopt_lock_t): _MyMutex(_Mtx){ // construct but don't lock}~lock_guard() noexcept{ // unlock_MyMutex.unlock();}lock_guard(const lock_guard&) = delete;lock_guard& operator=(const lock_guard&) = delete;
private:_Mutex& _MyMutex;};
针对这个源码,其中_MyMutex是私有成员变量。explicit表示禁止隐式转换。还涉及到构造函数重载、禁用构造、禁用拷贝函数。
2024年9月29日17:21:30
这篇文章是在之前写的,后面我又因为看代码疑惑信号量的使用,又写了下面的一篇文章:关于多线程unique_lock和guard_lock,而实际我在写关于多线程unique_lock和guard_lock这篇文章的时候也没有想起这边写的关于信号量的知识点。然后这次在看到这篇文章就想到了后面写的文章关于多线程unique_lock和guard_lock。但是我也忘了关于多线程unique_lock和guard_lock这篇文章的内容了。所以两篇文章结合看了下,加深了理解。
… …
七、 std::call_once与其使用场景
涉及到单例类。两种模式
饿汉模式和懒汉模式。
教程中以log类举例子,
static Log& GetInstance()
{static Log log;return log;
}
//这种是饿汉模式,构建类的时候就创建Log静态类对象。
//然后我的疑问是每次GetInstance的时候,不会多次创建log对象吗?实际是log是静态成员,只有一个
下面的是懒汉对象
static Log& GetInstance()
{static Log *log = nullptr;if(!log) {log = new Log();}return log;
}
上面代码我的疑问是,每次GetInstance的时候不会多次new对象吗?实际是不会,因为if(!log)做了判断,log不为空就不会再new对象。
针对饿汉模式在构造函数中创建静态变量,这边为什么不会继续创建对象,我查了资料,没看到啥解释。
但是看到另外两个点。
第一个就是懒汉模式申请的堆内存如果释放会内存泄漏。我觉得可以在析构函数释放就可以。
第二个就是帖子提到单例类模式线程不安全,主要是懒汉模式,多个线程读取if(!log)中log变量的时候,可能会多次申请堆内存。可以在if(!log)增加互斥锁,但是会影响效率。
参考文档:https://blog.csdn.net/code_feien/article/details/110423021
针对上述静态变量的问题,我还写了下面的测试代码
#include <iostream>using namespace std;int getData()
{static int test = 0;test++;return test;
}int main() {std::cout << "Hello, World!" << std::endl;for(int i = 0; i < 10; i++){int num = getData();cout << num << endl;}return 0;
}//预测结果是10
//实际打印结果1——10,确实和预期一样
//码可以理解为静态变量已经创建生命周期一直存在直到程序结束
九、 线程池
threadPool.h文件
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <queue>class ThreadPool {
public:ThreadPool(int numThreads) : stop(false) {for (int i = 0; i < numThreads; ++i) {threads.emplace_back([this] {while (true) {std::unique_lock<std::mutex> lock(mutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) {return;}std::function<void()> task(std::move(tasks.front()));tasks.pop();lock.unlock();task();}});}}~ThreadPool() {{std::unique_lock<std::mutex> lock(mutex);stop = true;}condition.notify_all();for (std::thread& thread : threads) {thread.join();}}template<typename F, typename... Args>void enqueue(F&& f, Args&&... args) {std::function<void()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));{std::unique_lock<std::mutex> lock(mutex);tasks.emplace(std::move(task));}condition.notify_one();}private:std::vector<std::thread> threads;std::queue<std::function<void()>> tasks;std::mutex mutex;std::condition_variable condition;bool stop;
};
main.cpp
#include "threadPool.h"int main(void)
{ThreadPool pool(4);for (int i = 0; i < 8; ++i) {pool.enqueue([i] {std::cout << "Task " << i << " is running in thread " << std::this_thread::get_id() << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Task " << i << " is done" << std::endl;});}return 0;
}
关于上述代码设计到lamda等知识点
9.1 std:bind
其中关于std::bind,见这篇文章: std::bind的讲解
9.2 std::forward
然后还有一个知识点就是std::forward,
参考这篇文章:std::forward与完美转发详解
然后我继续查找关于std::forward知识点,看到这个例子
std::forward入门
std::forward
是 C++11 引入的标准库函数,用于实现完美转发。完美转发意味着在函数模板内,保持传递给函数的参数的左右值属性和常量属性。下面是一个简单的
std::forward
使用示例:
#include <iostream>
#include <utility>// 这是一个函数模板,用来展示完美转发的效果
template<typename T>
void printValue(T&& val) {// 使用std::forward保留val的左右值属性和常量属性std::cout << (std::is_same<T, int&>::value ? "LValue: " : "RValue: ")<< std::forward<T>(val) << std::endl;
}int main() {int a = 5;printValue(a); // 将a作为左值传递printValue(std::move(a)); // 将a转换为右值,并传递return 0;
}
在这个例子中,printValue
是一个函数模板,它接受一个模板参数可以是任何类型的右值引用。当我们调用 printValue
时,我们可以传递一个左值或者右值。std::forward
保证在函数内部,我们传入的参数在模板实例化时保持其原有的左右值属性和常量属性。
输出结果将会是:
LValue: 5
RValue: 5
上述我不知道为什么上面的例子就能表现出std::forward的完美转发,我目前理解为std::forward可以实现左值和右值的转发。
然后上面有涉及到一个点就是std::is_same,关于std::is_same参考这篇文章:C++ 语言 std::is_same
9.3 std::function
std::function详解
我在纠结**std::function<void()> task(std::move(tasks.front()));**这个std::function<void()>是什么意思。查到下面的一个文章
c++ std::function的使用
其中一个示例
#include <functional>void function1()
{std::cout << "This is function1." << std::endl;
}int main()
{// 使用函数指针初始化 std::function 对象std::function<void()> f1 = function1;f1();
}//打印结果
//This is function1.
我理解大概意思就是std::function<void()>就是可以接收一个void f()函数。
9.4 对这个线程池的理解
lamda写法不是很理解,一直想找一个c++写的线程池代码没找到。
看了b站陈子青的视频,使用了他写的线程池,即有很多lamda表达式的线程池代码。可以实现900多个文件的正常读写。
总结
未完待续
相关文章:
C++11 多线程编程-小白零基础到手撕线程池
提示:文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问: 本文目标: 一、背景 来源于b站视频 C11 多线程编程-小白零基础到手撕线程池 学习来源:https://www.bilibili.com/video/BV1d841117SH/?p2&spm_id_f…...
智源研究院与百度达成战略合作 共建AI产研协同生态
2024年9月24日,北京智源人工智能研究院(简称“智源研究院”)与北京百度网讯科技有限公司(简称“百度”)正式签署战略合作协议,双方将充分发挥互补优势,在大模型等领域展开深度合作,共…...
Flask-SQLAlchemy:在Flask应用中优雅地操作数据库
在Python的Web开发领域,Flask是一个备受欢迎的轻量级Web框架,它以简洁、灵活而著称。而当我们需要在Flask应用中与数据库进行交互时,Flask-SQLAlchemy就成为了一个强大而便捷的工具。它将Flask的简洁性与SQLAlchemy的强大数据库抽象能力完美结…...
智能巡检机器人 数据库
智能巡检机器人AI智能识别。无需人工。只需后台监控结果即可!...
Spring AOP异步操作实现
在Spring框架中,AOP(面向切面编程)提供了一种非常灵活的方式来增强应用程序的功能。异步操作是现代应用程序中常见的需求,尤其是在处理耗时任务时,它可以帮助我们提高应用程序的响应性和吞吐量。Spring提供了一种简单的…...
【2006.07】UMLS工具——MetaMap原理深度解析
文献:《MetaMap: Mapping Text to the UMLS Metathesaurus》2006 年 7 月 14 日 https://lhncbc.nlm.nih.gov/ii/information/Papers/metamap06.pdf MetaMap:将文本映射到 UMLS 元数据库 总结 解决的问题 自动概念映射问题:解决如何将文本…...
ros2 colcon build 构建后,install中的local_setup.bash 和setup.bash有什么区别
功能概述 在 ROS2 中,colcon build是用于构建软件包的工具。构建完成后会生成install文件夹,其中的setup.bash和local_setup.bash文件都与环境设置相关,但存在一些区别。setup.bash 作用范围 setup.bash文件用于设置整个工作空间的环境变量。…...
Thymeleaf基础语法
Thymeleaf 是一种用于 Web 和非 Web 环境的现代服务器端 Java 模板引擎。它能够处理 HTML、XML、JavaScript、CSS 甚至纯文本。以下是 Thymeleaf 的一些基础语法: 1. 变量表达式 <!-- 显示变量的值 --> <p th:text"${name}">Default Name&l…...
spring cloud alibaba学习路线
以下是一条学习Spring Cloud Alibaba的路线: 一、基础前置知识 1. Java基础 熟练掌握Java语言特性,包括面向对象编程、集合框架、多线程等知识。 2. Spring和Spring Boot基础深入理解Spring框架,如依赖注入(DI)、控…...
基于 Seq2Seq 的中英文翻译项目(pytorch)
项目简介 本项目旨在使用 PyTorch 构建一个基于 Seq2Seq(编码器-解码器架构)的中英文翻译模型。我们将使用双语句子对的数据进行训练,最终实现一个能够将英文句子翻译为中文的模型。项目的主要步骤包括: 数据预处理:从数据集中提取英文和中文句子,并进行初步清洗和保存。…...
部标主动安全(ADAS+DMS)对接说明
1.前言 上一篇介绍了部标(JT/T1078)流媒体对接说明,这里说一下如何对接主动安全附件服务器。 流媒体的对接主要牵扯到4个方面: (1)平台端:业务端系统,包含前端呈现界面。 &#x…...
C++ STL(1)迭代器
文章目录 一、迭代器详解1、迭代器的定义与功能2、迭代器类型3、示例4、迭代器失效4.1、vector 迭代器失效分析4.2、list 迭代器失效分析4.3、set 与 map 迭代器失效分析 5、总结 前言: 在C标准模板库(STL)中,迭代器是一个核心概念…...
uview表单校验不生效问题
最近几次使用发现有时候会不生效,具体还没排查出来什么原因,先记录一下解决使用方法 <u--formlabelPosition"top"labelWidth"auto":model"form":rules"rules"ref"uForm" ><view class"…...
前端开发设计模式——单例模式
目录 一、单例模式的定义和特点: 1.定义: 2.特点: 二、单例模式的实现方式: 1.立即执行函数结合闭包实现: 2.ES6类实现: 三、单例模式的应用场景 1.全局状态管理: 2.日志记录器: …...
行情叠加量化,占据市场先机!
A股久违的3000点,最近都没有更新,现在终于对我们的市场又来点信息。相信在座的朋友这几天都是喜笑颜开,对A股又充满信心。当前行情好起来了,很多朋友又开始重回市场,研究股票学习量化,今天我们给大家重温下…...
大厂面试真题-ConcurrentHashMap怎么保证的线程安全?
ConcurrentHashMap是Java中的一个线程安全的哈希表实现,它通过一系列精妙的机制来保证线程安全。以下是ConcurrentHashMap保证线程安全的主要方式: 分段锁(Segment Locking,Java 1.8之前): 在Java 1.8之前的…...
【RabbitMQ】消息堆积、推拉模式
消息堆积 原因 消息堆积是指在消息队列中,待处理的消息数量超过了消费者处理能力,导致消息在队列中不断堆积的现象。通常有以下几种原因: 消息生产过快:在高流量或者高负载的情况下,生产者以极高的速率发送消息&…...
MySQL常用SQL语句(持续更新中)
文章目录 数据库相关表相关索引相关添加索引 编码相关系统变量相关 收录一些经常用到的sql 数据库相关 建数据库 CREATE DATABASE [IF NOT EXISTS] <数据库名> [[DEFAULT] CHARACTER SET <字符集名>] [[DEFAULT] COLLATE <校对规则名>];例如: C…...
【更新】红色文化之红色博物馆数据集(经纬度+地址)
数据简介:红色博物馆作为国家红色文化传承与爱国主义教育的重要基地,遍布全国各地,承载着丰富的革命历史与文化记忆。本数据说明旨在汇总并分析全国范围内具有代表性的红色博物馆的基本信息,包括其地址、特色及教育意义࿰…...
Python项目Flask框架整合Redis
一、在配置文件中创建Redis连接信息 二、 实现Redis配置类 import redis from config.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWD, REDIS_DB, EXPIRE_TIMEclass RedisDb():def __init__(self, REDIS_HOST, REDIS_PORT, REDIS_DB, EXPIRE_TIME, REDIS_PASSWD):# 建立…...
完整网络模型训练(一)
文章目录 一、网络模型的搭建二、网络模型正确性检验三、创建网络函数 一、网络模型的搭建 以CIFAR10数据集作为训练例子 准备数据集: #因为CIFAR10是属于PRL的数据集,所以需要转化成tensor数据集 train_data torchvision.datasets.CIFAR10(root&quo…...
高效便捷,体验不一样的韩语翻译神器
嘿,大家好啊!今天想跟大家聊聊我用过的几款翻译神器,特别是它们在翻译韩语时的那些小感受。作为一个偶尔需要啃啃韩语资料或者跟韩国朋友聊天的普通人,我真心觉得这些翻译工具简直就是我的救星! 一、福昕在线翻译 网址…...
Markdown笔记管理工具Haptic
什么是 Haptic ? Haptic 是一个新的本地优先、注重隐私的开源 Markdown 笔记管理工具。它简约、轻量、高效,旨在提供您所需的一切,而不包含多余的功能。 目前官方提供了 docker 和 Mac 客户端。 Haptic 仍在积极开发中。以下是未来计划的一些…...
网络原理-传输层UDP
上集回顾: 上一篇博客中讲述了应用层如何自定义协议:确定传输信息,确定数据格式 应用层也有一些现成的协议:HTTP协议 这一篇博客中来讲述传输层协议 传输层 socket api都是传输层协议提供的(操作系统内核实现的了…...
C++中,如何使你设计的迭代器被标准算法库所支持。
iterator(读写迭代器) const_iterator(只读迭代器) reverse_iterator(反向读写迭代器) const_reverse_iterator(反向只读迭代器) 以经常介绍的_DList类为例,它的迭代…...
Java NIO 全面详解:掌握 `Path` 和 `Files` 的一切
在 Java 7 中引入的 NIO (New I/O) 为文件系统和流的操作带来了强大的能力,其中 Path 和 Files 是核心部分。Path 作为对文件路径的抽象,提供了灵活的方式处理文件系统中的路径;Files 则通过一系列静态方法,使得文件的读写、复制、…...
bluez免提协议hands-free介绍,全到无法想象,bluez hfp ag介绍
零. 前言 由于Bluez的介绍文档有限,以及对Linux 系统/驱动概念、D-Bus 通信和蓝牙协议都有要求,加上网络上其实没有一个完整的介绍Bluez系列的文档,所以不管是蓝牙初学者还是蓝牙从业人员,都有不小的难度,学习曲线也相对较陡,所以我有了这个想法,专门对Bluez做一个系统…...
关于区块链的安全和隐私
背景 区块链技术在近年来发展迅速,被认为是安全计算的突破,但其安全和隐私问题在不同应用中的部署仍处于争论焦点。 目的 对区块链的安全和隐私进行全面综述,帮助读者深入了解区块链的相关概念、属性、技术和系统。 结构 首先介绍区块链…...
特征工程——一门提高机器学习性能的艺术
当前围绕人工智能(AI)和机器学习(ML)展开的许多讨论以模型为中心,聚焦于 ML和深度学习(DL)的最新进展。这种模型优先的方法往往对用于训练这些模型的数据关注不足,甚至完全忽视。类似MLOps的领域正迅速发展,通过系统性地训练和利用ML模型&…...
Paper解读:工作场所人机协作的团队形成:促进组织变革的目标编程模型
人工智能(AI)具有降低运营成本、提高效率和改善客户体验的潜力。 因此,在组织中组建项目团队至关重要,这样他们就会在决策过程中欢迎人工智能。 当前的技术革命要求公司快速变革,并增加了对团队在促进创新采用方面的作…...
网站建设推广文案/百度浏览器app下载
前言 在深入之前先了解下下ReentrantLock 和 Condition: 重入锁ReentrantLock: ReentrantLock锁在同一个时间点只能被一个线程锁持有;而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取。 ReentrantLock分为“公平锁”和“非公平锁”…...
怎样创建自己的网站/高端网站建设公司哪家好
vim /etc/hostname vim /etc/hosts reboot...
平板上做网站的软件/上海网络seo公司
如果是开平方根可以使用函数 SQRT(number),返回数值的平方根 比如SQRT(9) 计算返回3如果是开立方根或者n次方根,可以进e68a847a64364行幂运算,POWER(number,power)函数表示返回number数值的power次乘幂,如POWER(5,2)表示5的2次方&…...
做网站详情的图片/如何在网络上推广产品
拿得起,放得下,想得开 原文-中国传统文化:跟儒家学拿得起,跟佛家学放得下,跟道家学想得开 所谓道不远人、大道至简,作为中国文化三大支柱的儒释道,其实并不高高在上,而是与我们的人生…...
深圳专业定制建站公司/线上线下一体化营销
--查询指定供应商指定的一段时间内出票的张数 如果每查询一个月,修改一次时间太麻烦,写个循环的! declare date1 date declare date2 date declare startdate date declare enddate date declare countsum int declare count int set start…...
陕西省人民政府采购网/曲靖seo
本文同步发布于 个人博客 前言 上周周赛因为忘记起床导致没打TAT 本次周赛战绩: rk5,总完成时间20min,还有奖品,好耶! A 2129.将标题首字母大写 题意 给出一个包含若干个单词的句子,把所有字母变为小写字母&#…...