百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

C++11多线程知识点总结

mhr18 2025-05-14 14:57 20 浏览 0 评论

一、多线程的基本概念

1、进程与线程的区别和联系

进程:进程是一个动态的过程,是一个活动的实体。简单来说,一个应用程序的运行就可以被看做是一个进程;

线程:是运行中的实际的任务执行者。可以说,进程中包含了多个可以同时运行的线程。通俗理解:例如你打开微信就是打开一个进程,在微信里面和好友视频聊天就是开启了一条线程。

两者之间的关系:一个进程里面可以有多个线程,至少有一个线程。一个线程一定会在一个进程里面。

推荐视频:

聊聊腾讯面试linux后台开发被问到的多线程问题

6种epoll的设计,让你吊打面试官

学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

2、并发,并行的区别

并发:同一时间段内交替运行多个进程(线程);

并行:同一时刻运行多个进程(线程)。很明显,只有多处理器才能支持。

并发就像我们的大脑思考一样,同一个时刻只能想一件事,但是在很短的一个时间段内我们可以三心二意。当然如果你长了几个脑袋,那你就可以并行思考了。

3、同步与异步,阻塞与非阻塞方式

下面介绍同步,异步,阻塞,非阻塞这几个概念,加深对多线程编程的理解。

有了之前的概念,我们可以想象,当几个线程或者进程在并发执行时,如果我们不加任何干预措施,那么他们的执行顺序是由系统当时的环境来决定的,所以不同时间段不同环境下运行的顺序都会不尽相同,这便是异步(有差异的步骤)。当然,同步肯定就是通过一定的措施,使得几个线程或者进程总是按照一定顺序来执行(总是按照相同的步骤)。

当一个进程或者线程请求某一个资源而不得时,如I/O,便会进入阻塞状态,一直等待。scanf()便是一个很好的例子,当程序运行到scanf()时,如果输入缓存区为空,那么程序便会进入阻塞状态等待我们从键盘输入,这便是以阻塞的方式调用scanf()。通过一定方法,我们可以将scanf()变成非阻塞的方式来执行。如给scanf()设置一个超时时间,如果时间到了还是没有输入那么便跳过scanf(),这个时候我们就称为用非阻塞的方式来调用scanf()。

对比可以发现,同步即阻塞。想要按照某特定顺序来执行一系列过程,在上一个过程完成之前下一个过程必须等待,这就是阻塞在了这个地方。当同步运行的时候,会等待同步操作完成才会返回,否则会一直阻塞在同步操作处。

相反的,异步即非阻塞,当异步调用某个函数时,函数会立刻返回,而不会阻塞在那。

怎么判断异步操作是否已经完成?通常有3种方式:

1. 状态:异步操作完成时会将某个全局变量置为特定值,可以通过轮询判断变量的值以确定是否操作完成;

2. 通知:异步操作完成会给调用者发送特定信号;

3. 回调:异步操作完成时会调用回调函数。

所以同步即阻塞,异步即非阻塞。

4、线程阻塞的常见情况

1. 调用sleep()进入睡眠状态;

2. 用wait()暂停了线程,除非收到notify()唤醒线程;

3. 线程正在等待一些IO操作;

4. 线程正在试图调用被锁起来了的对象。

二、线程的几种状态转换

线程在一定条件下,状态会发生变化。线程一共有以下几种状态:

1. 新建状态(New):新创建了一个线程对象;

2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得;

3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码;

4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

5. 死亡状态(dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

【文章福利】需要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,内核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)

三、多线程与单线程

1、多线程与单线程的区别

单线程,顾名思义即是只有一条线程在执行任务,这种情况在我们日常的工作学习中很少遇到,所以我们只是简单做一下了解。

多线程,创建多条线程同时执行任务,这种方式在我们的日常生活中比较常见。但是,在多线程的使用过程中,还有许多需要我们了解的概念。比如,在理解上并行和并发的区别,以及在实际应用的过程中多线程的安全问题,对此,我们需要进行详细的了解。

多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题。

2、多线程是否一定比单线程效率高?

一提到多线程一般大家的第一感觉就是可以提升程序性能,在实际的操作中往往遇到性能的问题,都尝试使用多线程来解决问题,但多线程程序并不是在任何情况下都能提升效率,在一些情况下恰恰相反,反而会降低程序的性能。对于单核CPU计算密集型任务,多线程反而并不能带来效率的提升。线程本身由于创建和切换的开销,采用多线程不会提高程序的执行速度,反而会降低速度。但是对于频繁IO操作的程序,多线程可以有效的并发。对于包含不同任务的程序,可以考虑每个任务使用一个线程。这样的程序在设计上相对于单线程做所有事的程序来说,更为清晰明了,比如生产、消费者问题。

在实际的开发中对于性能优化的问题需要考虑到具体的场景来考虑是否使用多线程技术。

四、为什么要使用多线程

1、什么时候使用多线程?

线程必然不是越多越好,线程切换也是要开销的,当你增加一个线程的时候,增加的额外开销要小于该线程能够消除的阻塞时间,这才叫物有所值。

什么时候该使用多线程呢?这要分四种情况讨论:

1. 多核CPU——计算密集型任务。此时要尽量使用多线程,可以提高任务执行效率,例如加密解密,数据压缩解压缩(视频、音频、普通数据),否则只能使一个核心满载,而其他核心闲置;

2. 单核CPU——计算密集型任务。此时的任务已经把CPU资源100%消耗了,就没必要也不可能使用多线程来提高计算效率了;相反,如果要做人机交互,最好还是要用多线程,避免用户没法对计算机进行操作;

3. 单核CPU——IO密集型任务,使用多线程还是为了人机交互方便;

4. 多核CPU——IO密集型任务,这就更不用说了,跟单核时候原因一样。

2、什么时候不使用多线程?

知道什么情况下不使用并发同样重要。从根本上来说,不使用并发的唯一原因就是并发带来的效益小于它带来的代价。在许多情况下,使用并发会使代码难以理解,编写、维护并发代码需要更多的脑力成本,并发带来的复杂性可能会增加bug。除非并发带来性能的提升足够打,或者模块划分足够清楚,否则不要使用并发。

使用并发带来性能上的提升可能不如预期。并发编程也需要额外的开销,在创建一个线程时,系统要分配内核资源、栈空间,然后把新线程加入到任务队列。如果线程运行时间小于线程的创建时间,这时使用多线程可能会使性能变差。

进一步来说,线程资源是有限的。如果同时有太多线程,会占用太多系统资源,会使整个系统变慢。使用太多线程会消耗尽内存或处理器的地址空间,因为线程需要独立的栈空间。

在C/S架构下,如果为每个连接创建一个线程,在连接比较少时性能很好,但是如果要同时处理太多连接的话会创建太多线程。这时使用线程池(thread pools)可以提高性能。在Linux下可以使用I/O多路复用:select、poll、epoll。

线程的切换也需要时间,如果线程切换时间比线程运行时间还短,就会降低整体性能。

五、多线程优缺点

1、优点

1. 提高CPU的使用率:例如朋友圈发表图片,当你上传9张图片的时候,如果开启一个线程用同步的方式一张张上传图片,假设每次上传图片的线程只占用了CPU 1%d的资源,剩下的99%资源就浪费了。但是如果你开启9个线程同时上传图片,CPU就可以使用9%的资源了;

2. 提高程序的工作效率:还是拿朋友圈发表图片来说,假设开启一个线程上传一张图片的时间是1秒,那么同步的方式上传9张就需要9秒,但是你开启9个线程同时上传图片,那么就只需要1秒就完成了。

2、缺点

1. 如果有大量的线程,会影响性能,因为CPU需要在它们之间切换;

2. 更多的线程需要更多的内存空间;

3. 多线程操作可能会出现线程安全或者死锁等问题。

六、线程安全及解决方法

线程安全:简单的来说,就是在多个线程访问一个类的时候,该类始终保持着正确的执行行为。

1、线程安全出现的根本原因

1. 存在两个或者两个以上的线程对象共享同一个资源;

2. 多线程操作共享资源代码有多个语句。

2、线程安全的解决方法

1. 无状态对象永远是线程安全的;

2. 原子性与竞争条件:原子性,顾名思义,指的就是不可分割的操作,多个操作要么一起执行,要么就都不执行。原子性保证了程序的执行不会因为执行的时序问题而引发的线程安全问题。而经常引起原子性问题的就是竞争条件。比如常见的检查再运行(check-then-act),当我创建一个文件夹时,会先判断文件夹是否存在,不存在再创建。这个在单线程的情况下不会出现问题,但是在多线程下,就可能会因为当我检查文件夹不存在后,另一个线程先创建了该文件夹,从而导致此线程创建文件错误。

相关推荐

一文带您了解数据库的行列之争:行式与列式存储的异同

数据库存储格式是数据库管理系统中一个至关重要的方面,它直接影响到数据的组织和检索效率。在数据库中,有两种主要的存储格式,即行式存储和列式存储。这两者采用截然不同的方法来组织和存储数据,各自具有一系列优...

NL2SQL(三)开源项目怎么选:talk is cheap, show me the code!

老规矩,先看效果下面的demo来自试用的SuperSonic,将会在下面详细介绍:大模型时代Text-to-SQL特点随着基于LLM技术的发展,RAG/AIAgent/Fine...

JDK25长期支持版九月降临:18项王炸功能全解析

Java要放大招啦!9月份推出的JDK25长期支持版已经锁定18个超能力,从稳定值到结构化并发,还有Linux系统下的"预知未来"性能分析!下面我用打游戏的术语给你们掰扯明白:1、飞...

OceanBase 推出单机版 高度兼容MySQL和Oracle

【环球网科技综合报道】3月27日,独立数据库厂商OceanBase正式发布单机版产品。据悉,这一产品基于自主研发的单机分布式一体化架构设计,具备极简数据库架构和高度兼容性,为中小规模业务提供兼具性能与...

黄远邦:应对7月1日闰秒对Oracle数据库影响

由于今年7月1日全世界会多出一秒,这可能对时间敏感的IT系统造成较大影响。中亦科技数据库团队对此问题做了深入的研究,并对用户系统提出了相应的解决方法及建议。中亦科技数据库产品总监黄远邦认为,闰秒调整会...

MySQL数据库密码忘记了,怎么办?(mysql 数据库密码)

#头条创作挑战赛#MySQL数据库密码忘记了且没有其他可以修改账号密码的账户时怎么办呢?登录MySQL,密码输入错误/*密码错误,报如下错误*/[root@TESTDB~]#mysql-u...

Chinese AI Talent in Spotlight as Nvidia and Meta Escalate Talent War

OntherightisBanghuaZhu,ChiefResearchScientistatNVIDIATMTPOST--SiliconValley’stoptech...

用Cursor开启JAVA+AI生涯(javascirpt怎么开启)

Cursor是基于VSCode开发的一款编辑器,支持多种语言的开发编辑。与传统的开发工具相比,它有多种优势:与AI无缝集成,响应速度快,占用内存小。但很多同学在"起步"过程中遇到了...

毕业十年了,自从做了开发用了很多软件,但距离写开发工具还很远

办公系统类:办公软件Word、Excel、PowerPoint三大必备技能+腾讯/金山在线文档解压缩操作:7-zip/winrar文件文本处理:Notepad++(文本编辑器正则表达式超级好...

盘点Java中最没用的知识⑤:这3个老古董你还在代码里“考古”?

一、Stack类:“继承Vector”的历史bug,为何成了性能拖油瓶?你是不是在学Java集合时,老师说过“栈结构用Stack类”?是不是在老代码里见过"newStack<>(...

Gemini 2.5 Pro 0506发布,编程最强大模型, 碾压 Claude3.7 sonnent

一、Gemini2.5Pro(I/Oedition)发布1、为何叫I/Oedition?谷歌史上最强编程模型Gemini2.5Pro(I/Oedition)发布,具体型号是Gemin...

如何让无聊变得有趣(附本人大量美图)

文/图:金冬成在这条长300公里的公路上,我已经来回往返了无数次。3小时车程,一个人,想想都是多么无聊的一件事。其实,人生道路上,类似这种无聊的事情有很多很多。无聊的事情、枯燥的工作,往往让我们容易失...

Oracle 推出 Java 24,增强 AI 支持和后量子加密

导读:Oracle宣布正式发布Java24,该语言增加了几个新功能,例如StreamGatherersAPI和Class-FileAPI的可用性,以及专门为AI推理和量子安全设计...

公司ERP突然变慢?“索引重建”这颗“药”可不能随便吃!

各位老板、IT小哥、财务小姐姐,有没有遇到过公司ERP系统突然卡顿得像“老爷车”,点个按钮半天没反应,急得直跺脚?这时候,可能有人会跳出来说:“我知道,重建一下数据库索引就好了!”听起来像个“神操作”...

基于Java实现,支持在线发布API接口读取数据库,有哪些工具?

基于java实现,不需要编辑就能发布api接口的,有哪些工具、平台?还能一键发布、快速授权和开放提供给第三方请求调用接口的解决方案。架构方案设计:以下是一些基于Java实现的无需编辑或只需少量编辑...

取消回复欢迎 发表评论: