Studying-多线程学习Part1-线程库的基本使用、线程函数中的数据未定义错误、互斥量解决多线程数据共享问题
来源:多线程编程
线程库的基本使用
两个概念:
- 进程是运行中的程序
- 线程是进程中的进程
串行运行:一次只能取得一个任务并执行这一个任务
并行运行:可以同时通过多进程/多线程的方式取得多个任务,并以多进程或多线程的方式同时执行这些任务。
线程的最大数量取决于cpu的核心数。
thread的函数原型:传入一个函数名就可以运行
![]()
1.创建线程:
使用thread函数,但是如下程序会报错,原因在于线程还在运行的时候,主程序可能就已经结束了。
#include <iostream>
#include <thread>
using namespace std;void printHelloWorld() {cout << "Hello World" << endl;return;
}int main() {//1.创建线程thread thread1(printHelloWorld);return 0;
}
2.等待线程完成:
为了解决上述问题,我们可以使用join()函数,来让主线程等待线程执行完毕。
PS:join()函数是阻塞的,程序会一直停留在join()处,直到线程运行完毕
#include <iostream>
#include <thread>
using namespace std;void printHelloWorld() {cout << "Hello World" << endl;return;
}int main() {//1.创建线程thread thread1(printHelloWorld);//主程序等待线程执行完毕join()thread1.join();return 0;
}
3.传入参数:
如果函数带有参数,我们也可以在thread的后面增加参数列表。
#include <iostream>
#include <thread>
#include <string>
using namespace std;void printHelloWorld(string msg, string msg2) {cout << msg << endl;cout << msg2 << endl;return;
}int main() {//1.创建线程thread thread1(printHelloWorld, "Hello Thread", "Hello World");//主程序等待线程执行完毕join()thread1.join();return 0;
}
4.分离线程:
我们有时候也希望主程序不需要等待线程完成,而是让线程它在后台运行,这时候我们可以使用到detach()函数分离线程。(下述情况什么都不会打印,来不及打印)
#include <iostream>
#include <thread>
#include <string>
using namespace std;void printHelloWorld(string msg, string msg2) {cout << msg << endl;cout << msg2 << endl;return;
}int main() {//1.创建线程thread thread1(printHelloWorld, "Hello Thread", "Hello World");//主程序等待线程执行完毕join()thread1.detach();return 0;
}
5. joinable():
该函数返回一个布尔值,如果线程可以被join()或detach(),则返回true,否则返回false。如果我们试图对一个不可加入的线程调用join()或detach(),则会抛出一个std::system_error异常。
#include <iostream>
#include <thread>
#include <string>
using namespace std;void printHelloWorld(string msg, string msg2) {cout << msg << endl;cout << msg2 << endl;return;
}int main() {//1.创建线程thread thread1(printHelloWorld, "Hello Thread", "Hello World");//主程序等待线程执行完毕join()bool isJoin = thread1.joinable();if (isJoin) {thread1.join();}return 0;
}
线程函数中的数据未定义错误
1.传递临时变量的问题:
#include <iostream>
#include <thread>
using namespace std;void foo(int& x) {x += 1;
}int main() {thread t(foo, 1);t.join();return 0;
}
在上述例子中,我们将临时变量1作为参数传递给了foo, 这样会导致在线程函数执行时,临时变量`1`已经销毁,从而导致未定义行为。
解决方案是将变量复制到一个持久的对象中,然后将该对象传递给线程。例如,我们可以将`1`复制到一个`int`类型的变量中,然后将该变量的引用传递给线程。
#include <iostream>
#include <thread>
using namespace std;void foo(int& x) {x += 1;
}int main() {int a = 1;//ref函数,将a转换为自身的引用,在线程函数中需要使用thread t(foo, ref(a));t.join();cout << a << endl;return 0;
}
2. 传递指针或引用指向局部变量的问题:
#include <iostream>
#include <thread>
using namespace std;thread t;
void foo(int& x) {x += 1;
}void test() {int a = 1;t = thread(foo, ref(a));return;
}int main() {test();t.join();return 0;
}
如果传入线程中的参数是局部变量,则线程在进行的时候,变量就已经被销毁了,无法得到结果。解决办法就是让a变量变为全局变量。或者join()放在thread()函数后面。关键就在于要注意变量的生命周期。
3. 传递指针或引用指向已释放的内存的问题:
#include <iostream>
#include <thread>
using namespace std;void foo(int* x) {cout << *x << endl; // 访问已经被释放的内存
}
int main() {int* ptr = new int(1);thread t(foo, ptr); // 传递已经释放的内存delete ptr;t.join();return 0;
}
提前把ptr进行删除,会导致传入的是已经释放内存了的空间,结果变得不确定。这也是要注意变量的生命周期和作用范围的问题。
4. 类成员函数作为入口函数,类对象被提前释放
和上一个问题的原因类似,在创建线程之后,如果类对象已经被销毁,这会导致在线程执行时无法访问对象,可能会导致程序崩溃或者产生未定义的行为。
#include <iostream>
#include <thread>
using namespace std;class A {
public:void foo() {cout << "Hello" << endl;}
};int main() {A a;thread t(&A::foo, &a);/*进行一系列操作,可能会导致a被释放*/t.join();return 0;
}
为了解决上述问题,我们需要使用指针来保证地址有效,但如果用普通指针,我们需要自行进行指针的删除和释放。因此我们可以采用智能指针shared_ptr来管理类对象的生命周期,确保在线程执行期间对象不会被销毁。具体来说,可以在创建线程之前,将类对象的指针封装在shared_ptr 对象中,并将其作为参数传递给线程。这样,在线程执行期间,即使类对象的所有者释放了其所有权。
#include <iostream>
#include <thread>
#include <memory>
using namespace std;class A {
public:void foo() {cout << "Hello" << endl;}
};int main(){shared_ptr<A> a = make_shared<A>();thread t(&A::foo, a);/*进行一系列操作,可能会导致a被释放*/t.join();return 0;
}
5.入口函数为类的私有成员函数
#include <iostream>
#include <thread>
using namespace std;class A {
private:void foo() {cout << "Hello" << endl;}
};int main() {shared_ptr<A> a = make_shared<A>();thread t(&A::foo, a); //报错不能调用foo函数/*进行一系列操作,可能会导致a被释放*/t.join();return 0;
}
如果函数是私有成员函数,那么是无法调用的。需要使用到友元函数, 并在函数中调用foo()函数。
#include <iostream>
#include <thread>
using namespace std;class A {
private:friend void thread_foo();void foo() {cout << "Hello" << endl;}
};void thread_foo() {shared_ptr<A> a = make_shared<A>();thread t(&A::foo, a); //报错不能调用foo函数/*进行一系列操作,可能会导致a被释放*/t.join();return;
}int main() {thread_foo();return 0;
}
互斥量解决多线程数据共享问题
数据共享问题分析
在多个线程中共享数据时,需要注意线程安全问题。如果多个线程同时访问同一个变量,并且其中至少有一个线程对该变量进行了写操作,那么就会出现数据竞争问题。数据竞争可能会导致程序崩溃、产生未定义的结果,或者得到错误的结果。
数据竞争问题简单的可以理解为,t1和t2在取数据的时候,可能会取到同样的a的值,也就是t2不会等待t1完成对a的所有加1操作之后,才去取a,这样就会导致a无法正确的加200000次。
#include <iostream>
#include <thread>
using namespace std;
int share_data = 0;
void fun() {for (int i = 0; i < 100000; ++i) {share_data += 1;}
}int main() {thread t1(fun);thread t2(fun);t1.join();t2.join();cout << share_data << endl;return 0;
}
为了解决这个问题,我们需要保证当一个线程去拿a的值的时候,其他的线程不能去拿a,保证共享数据的安全。 为了避免数据竞争问题,需要使用同步机制来确保多个线程之间对共享数据的访问是安全的。常见的同步机制包括互斥量、条件变量、原子操作等。
互斥锁
互斥量(mutex)是一种用于实现多线程同步的机制,用于确保多个线程之间对共享资源的访问互斥。互斥量通常用于保护共享数据的访问,以避免多个线程同时访问同一个变量或者数据结构而导致的数据竞争问题。互斥量提供了两个基本操作:lock() 和 unlock()。当一个线程调用 lock() 函数时,如果互斥量当前没有被其他线程占用,则该线程获得该互斥量的所有权,可以对共享资源进行访问。如果互斥量当前已经被其他线程占用,则调用 lock() 函数的线程会被阻塞,直到该互斥量被释放为止。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;int share_data = 0;
mutex mtx;
void fun() {for (int i = 0; i < 1000000; ++i) {mtx.lock(); //加锁操作share_data += 1;mtx.unlock(); //解锁操作}
}int main() {thread t1(fun);thread t2(fun);t1.join();t2.join();cout << share_data << endl;return 0;
}
本质上,互斥锁并没有对share_data加锁,而是在每次运行+= 1操作之前,都会判断mtx互斥量是否上锁,如果上锁了则阻塞等待,直到另一边解锁,以此来实现对shared_data变量的访问是安全的。
线程安全定义:
如果多线程程序每一次的运行结果和单线程运行的结果始终是一样的,那么你的线程就是安全的。也就是把多线程改为单线程之后,运行的结果也不会发生改变。(本质上多线程就是把单线程的任务分割为多个任务,能够安全的分别进行,以此来增加程序的运行效率)
相关文章:
Studying-多线程学习Part1-线程库的基本使用、线程函数中的数据未定义错误、互斥量解决多线程数据共享问题
来源:多线程编程 线程库的基本使用 两个概念: 进程是运行中的程序线程是进程中的进程 串行运行:一次只能取得一个任务并执行这一个任务 并行运行:可以同时通过多进程/多线程的方式取得多个任务,并以多进程或多线程…...
Flink 03 | 数据流基本操作
Flink数据流结构 DataStream 转换 通常我们需要分析的业务数据可能存在如下问题: 数据中包含一些我们不需要的数据 数据格式不方面分析 因此我们需要对原始数据流进行加工,比如过滤、转换等操作才可以进行数据分析。 “ Flink DataStream 转换主要作…...
在 TS 的 class 中,如何防止外部实例化
在 TypeScript(TS)中,如果你想要防止一个类被外部实例化,你可以采取以下几种策略: 将构造函数设为私有(Private Constructor): 通过将类的构造函数设为私有,你可以阻止外…...
HTML详解
HTML 基础HTML 标题HTML 段落HTML 链接HTML 图片HTML 元素HTML 注释HTML 属性HTML 文本格式化HTML 头部HTML cssHTML 表格HTML 列表HTML 自定义列表HTML 区块HTML 表单HTML 框架HTML 颜色HTML 脚本HTML 事件HTML 实体HTML urlHTML5 新元素 新元素 新元素 新元素 新元素 新元素 …...
记录|Modbus-TCP产品使用记录【德克威尔】
目录 前言一、德克威尔1.1 实验图1.2 DECOWELL IO Tester 软件1.3 读写设置1.4 C#进行Modbus-TCP读写 更新时间 前言 参考文章: 使用的第二款Modbus-TCP产品。 一、德克威尔 1.1 实验图 1.2 DECOWELL IO Tester 软件 这也是自带模块配置软件的。下图就是德克威尔的…...
基于深度学习的视频生成
基于深度学习的视频生成是一项极具前景的技术,旨在通过神经网络模型生成逼真的动态视频内容。随着生成对抗网络(GANs)、自回归模型、变分自编码器(VAEs)等深度学习模型的发展,视频生成技术已经取得了显著进…...
TB6612电机驱动模块(STM32)
目录 一、介绍 二、模块原理 1.原理图 2.电机驱动原理 三、程序设计 main.c文件 Motor.h文件 Motor.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 TB6612FNG 是东芝半导体公司生产的一款直流电机驱动器件,它具有大电流 MOSFET-H 桥结构ÿ…...
webpack信息泄露
先看看webpack中文网给出的解释 webpack 是一个模块打包器。它的主要目标是将 JavaScript 文件打包在一起,打包后的文件用于在浏览器中使用,但它也能够胜任转换、打包或包裹任何资源。 如果未正确配置,会生成一个.map文件,它包含了原始JavaScript代码的映…...
启动服务并登录MySQL9数据库
【图书推荐】《MySQL 9从入门到性能优化(视频教学版)》-CSDN博客 《MySQL 9从入门到性能优化(视频教学版)(数据库技术丛书)》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) Windows平台下安装与配置MyS…...
微服务_3.微服务保护
文章目录 一、微服务雪崩及解决方法1.1、超时处理1.2、仓壁模式1.3、断路器1.4、限流 二、Sentinel2.1、流量控制2.1.1、普通限流2.1.2、热点参数限流 2.2、线程隔离2.3、熔断降级2.3.1、断路器状态机2.3.2、断路器熔断策略2.3.2.1、慢调用2.3.2.2、异常比例,异常数…...
【设计模式】软件设计原则——依赖倒置合成复用
依赖倒置引出 依赖倒置 定义:高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。面向接口编程而不是面向实现编程。 通过抽象使用抽象类、接口让各个类or模块之间独立不影响,实现…...
vue中如何实现组件通信
1. 父子组件通信 1. props和emits 我们最常见的组件通信就是父子组件数据通信。父子组件实现数据通信需要使用props和emit两个api。 在父组件中我们通过props将数据绑定给子组件,在子组件中我们可以通过props对象来收集到父组件传递的数据。 在子组件想要修改的pr…...
C/C++:内存管理
文章目录 前言一、内存分区1. 内存划分情况2. 最大内存计算 二、malloc/calloc/realloc 与 free1. malloc2. calloc3. realloc4. free5. 差异对比6. 失败处理 三、内存分配题目1. 题目2. 内存区域划分 四、C内存管理方式1. new 与 delete2. new/delete操作内置类型3. new和dele…...
jmeter学习(4)提取器
同线程组https://blog.csdn.net/vikeyyyy/article/details/80437530 不同线程组 在JMeter中,正则表达式提取的参数可以跨线程组使用。 通过使用Beanshell后置处理器和属性设置函数,可以将提取的参数设置为全局变量,从而在多个线程组之间共享…...
移动端的每日任务,golang后端数据库应该怎么设计
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...
1、Spring Boot 3.x 集成 Eureka Server/Client
一、前言 基于 Spring Boot 3.x 版本开发,因为 Spring Boot 3.x 暂时没有正式发布,所以很少有 Spring Boot 3.x 开发的项目,自己也很想了踩踩坑,看看 Spring Boot 3.x 与 2.x 有什么区别。自己与记录一下在 Spring Boot 3.x 过程…...
Vue根实例、实例总结
在Vue.js框架中,根实例和实例扮演着至关重要的角色。以下是对Vue根实例和实例的总结: Vue根实例 定义与创建: Vue根实例是Vue.js应用的核心。每个Vue应用都是通过用Vue函数创建一个新的Vue实例开始的,这个实例被称为根实例。根实…...
微服务架构:Spring Cloud的服务注册与发现、配置管理、服务网关、熔断器、分布式追踪
微服务架构是一种将应用程序构建为一组小型、自治的服务的方法,每个服务都运行在其独立的进程中,服务间通过轻量级通信机制(通常是HTTP API)进行通信。Spring Cloud是一套基于Spring Boot的微服务解决方案,它提供了一系…...
Spring Boot实现的大学生就业市场解决方案
1系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,这种种好处使得互联网成了信息传…...
Ubuntu上安装Git:简单步骤指南
Git是目前世界上最流行的版本控制系统,广泛用于软件开发中。无论你是开发者还是版本控制的新手,Git都是你不可或缺的工具。本文将为你介绍如何在Ubuntu操作系统上安装Git。 什么是Git? Git是一个开源的分布式版本控制系统,由Lin…...
Qwen3.5-2B轻量化技术解析:模型剪枝+KV Cache优化如何降低70%显存占用
Qwen3.5-2B轻量化技术解析:模型剪枝KV Cache优化如何降低70%显存占用 1. 轻量化模型的核心价值 在AI模型部署领域,大模型的资源消耗一直是阻碍其广泛应用的瓶颈。Qwen3.5-2B作为一款仅20亿参数的多模态基础模型,通过创新的轻量化技术实现了…...
卡证检测矫正模型中小企业降本:替代万元级专用证件扫描仪方案
卡证检测矫正模型:中小企业降本利器,替代万元级专用证件扫描仪方案 1. 引言:一个被忽视的降本痛点 如果你在中小企业负责行政、人事或财务,一定对下面这个场景不陌生:每天要处理一堆身份证、护照、驾照的复印件或扫描…...
告别软件盗版烦恼:用YT88加密狗5分钟搞定C#/Java/Python源代码加密(附完整开发包下载)
5分钟实现多语言源代码加密:YT88加密狗实战指南 独立开发者最头疼的问题之一,就是辛苦编写的代码被轻易反编译或盗用。上周我的一个朋友就遇到了这种情况——他花了三个月开发的Python数据分析工具,刚上线两周就被破解并免费传播。这种经历在…...
告别双流!用Vision Transformer (ViT) 搭建单流目标跟踪器OSTrack,实测速度提升40%
单流目标跟踪新范式:ViT驱动的OSTrack实战解析 在计算机视觉领域,目标跟踪技术正经历着从传统双流架构向单流范式的革命性转变。当我们面对复杂场景中的实时跟踪需求时,传统方法的性能瓶颈日益凸显——特征提取与关系建模的割裂处理导致计算冗…...
Whisper语音识别镜像快速部署:5分钟搭建多语言客服转写服务
Whisper语音识别镜像快速部署:5分钟搭建多语言客服转写服务 1. 引言:语音识别在客服场景的挑战 在全球化的商业环境中,客服中心面临着多语言支持的巨大挑战。传统语音识别系统往往需要为每种语言单独部署模型,不仅成本高昂&…...
Pixel Aurora Engine部署教程:Nginx反向代理+HTTPS配置像素AI服务公网访问
Pixel Aurora Engine部署教程:Nginx反向代理HTTPS配置像素AI服务公网访问 1. 项目介绍与准备 Pixel Aurora Engine是一款基于AI扩散模型的高端像素艺术生成工具,采用复古8-bit游戏风格界面设计。通过本教程,您将学会如何通过Nginx反向代理和…...
Python MCP服务部署卡在step3?揭秘92%开发者忽略的config.toml权限校验机制(配置失效终极诊断指南)
第一章:Python MCP服务部署卡在step3的典型现象与问题定位当执行 Python MCP(Model Control Platform)服务自动化部署脚本时,step3(即服务容器化构建与镜像推送阶段)常出现长时间无响应、日志停滞于 Buildi…...
Z-Image-Turbo商业应用探索:稳定可靠的AI绘画方案推荐
Z-Image-Turbo商业应用探索:稳定可靠的AI绘画方案推荐 1. 商业级AI绘画的新选择 在数字内容创作需求爆炸式增长的今天,Z-Image-Turbo作为阿里通义实验室开源的文生图模型,凭借其卓越的稳定性和高效性,正在成为商业应用领域的新宠…...
3种革命性技术突破:解放城通网盘下载速度的终极方案
3种革命性技术突破:解放城通网盘下载速度的终极方案 【免费下载链接】ctfileGet 获取城通网盘一次性直连地址 项目地址: https://gitcode.com/gh_mirrors/ct/ctfileGet 你是否曾经面对城通网盘那令人绝望的下载速度而束手无策?当急需获取重要文件…...
圆形光斑激光熔覆 Comsol 仿真:科研利器已就位
圆形光斑激光熔覆comsol仿真模型,模型已通过实验验证了正确性,确保模型一定正确可用于科研。 高斯热源,马兰戈尼效应,粘性耗散力等,激光熔覆过程必要项均考虑在模型中。 可根据自己需要调整工艺参数,做完对…...
