C++多线程(并发、进程、线程的基本概念和综述)

news2024/10/7 4:28:57

并发、进程、线程的基本概念和综述

并发

并发表示两个或者更多任务(独立的活动)同时发生(进行)。例如,一面唱歌一面弹琴,一面走路一面说话,画画的时候听小说等。回归到计算机领域,所谓并发,就是一个程序同时执行多个独立的任务。

以往计算机只有单核CPU(中央处理器)的时候,这种单核CPU某一个时刻只能执行一个任务,它实现多任务的方式就是由操作系统调度,每秒钟进行多次所谓的“任务切换”,也就是这个任务做一小会如做10ms,再切换到下个任务再做10ms等,诸如此类。因为任务切换的速度很快,所以在人类的感觉中,好像是多个任务在同时进行中(并行执行),其实这是一种并发的假象(不是真正的并发)。当然,这种任务之间的切换(也称上下文切换)也有一定的时间开销,例如操作系统要保存任务切换时的各种状态、执行进度等信息,因为一会儿任务切换回来的时候要复原这些信息。

随着计算机硬件的发展,专门用于服务器和高性能计算领域的“多处理器计算机”,甚至是家用台式机等均已出现,早先都是单核CPU,而现在在一块芯片上有多个CPU,双核、四核屡见不鲜,甚至还有8核、10核甚至更多核等。这些多处理器计算机以及多核计算机能够真正实现并行执行多个任务(这叫硬件并发),因为有多个CPU,就可以同时做多件事情。当然,如果并发的任务数量超过了CPU的数量,如现在有10个任务,但却只是双核CPU,那任务切换这种事情肯定还是存在的。而当代的计算机,不难发现它们同时都在处理几百上千个任务,例如打开任务管理器就可以看到,如图所示,其中线程数量就可以理解为并发的任务数量。

image-20221205193640208

例如有4个任务,CPU是一个双核CPU,任务切换如图所示(当然可能任务切换也不一定是这样的平均和均衡,因为操作系统的任务调度算法是很复杂的)。

image-20221205163810818

可以看到,使用并发的原因主要就是能够让多件事情同时做,从而提高整体做事情的效率,也就是提高整体的运行性能。

可执行程序

可执行程序就是磁盘上的一个文件(也叫程序),如在Windows操作系统下,一个扩展名为. exe的程序一般就是一个可执行程序。而在Linux操作系统下,有可执行权限的文件如权限是-rwxrw-r--,这里的x表示的就是可执行权限,有这种权限的文件一般就是可执行程序。

进程

知道了什么叫可执行程序,可执行程序当然是能运行的,在Windows操作系统下,双击一个. exe可执行程序,这个程序就运行起来了;又如,在Visual Studio 2019中,使用Ctrl+F5快捷键也可以运行程序;而在Linux操作系统下,如一个可执行程序叫作a,那么,输入./a,然后按Enter键就可以把这个可执行程序运行起来。

这样就引出了进程的概念:一个可执行程序运行起来,这就叫创建了一个进程。如果再次运行这个可执行程序,就又创建了一个进程,如此反复,多次运行一个可执行程序,就可以创建出多个进程。

所以,进程就是运行起来了的可执行程序。

线程

请先记住两件事:

  • 每个进程都有一个主线程,这个主线程是唯一的,也就是一个进程中只能有一个主线程。 (自带)
  • 当运行一个可执行程序,产生了一个进程之后,这个主线程就随着这个进程默默启动起来了。

这里以创建的MyProject项目为例说明一下。

当按Ctrl+F5键执行MyProject项目的可执行程序(编译链接后会生成可执行程序)后,程序从main主函数开始执行代码,一直遇到main 主函数中的return 0;语句行,从而结束整个程序的运行。实际上是由主线程来执行main主函数中的代码,如图所示。

image-20221205194234148

所以要把线程理解成一条代码的执行通路(道路)。

这就好比从北京到深圳,这里有一条道路,主线程走的是这条道路。

在这里,北京就相当于执行步骤main主函数的开始代码,深圳就相当于main主函数的return0;结束代码。要从main主函数的开始代码执行,一直执行到main函数的结束代码return0;为止。

除了主线程之外,可以通过编写代码来创建其他线程,其他线程可以走别的道路,去不同的地方。例如,从北京到南京就是一条新的道路,这就是上面讲的并发的概念,非常简单。每创建一个新线程,就可以在同一时刻多做一件不同的事情。

当然,线程并不是越多越好,每个线程都需要一个独立的堆栈空间(耗费内存,如一个线程占用1MB堆栈空间),而且线程之间的切换也要保存很多中间状态等,这也涉及上面提到过的上下文切换。所以,如果线程太多,上下文切换的就会很频繁,而上下文切换是一种必须但是没有价值和意义的额外工作,会耗费本该属于程序运行的时间。

举个最实际的例子,例如要开发一个游戏服务器,同时针对100个玩家提供服务,其中有一个玩家(玩家1)要充值,充值需要游戏服务器去联络充值服务器,如图所示。

image-20221205170203809

那么,和充值服务器通信、充值、充值服务器反馈假如大概需要10s的等待时间(一般来说,手机游戏充值,都需要10~20s甚至更长的等待时间),等待充值服务器给玩家1反馈信息。那么,假如在等待的10s时间内,另一个玩家(玩家2)如果有一个新需求,玩过手机游戏的读者都知道,如玩家2要抽一张卡,那作为游戏服务器的开发者总不可能让玩家2等10s,等玩家1充值完再为玩家2提供抽卡服务,否则玩家2肯定要抓狂了。

所以,游戏服务器必须采用多线程方式来处理多个玩家的各种不同需求。如图所示,一个线程处理玩家1的充值,另外一个线程处理玩家2的抽卡,两者互不影响,同时进行。

image-20221205170330920

对线程做一个总结:

  • 线程是用来执行代码的。
  • 把线程理解成一条代码的执行通路(道路),一个新线程代表一条新的通路。
  • 一个进程自动包含一个主线程,主线程随着进程默默启动并运行,并可以包含多个其他线程(非主线程,需要用代码来创建其他线程),但创建线程的数量最大一般都不建议超过200~300个,至于到底多少个合适,在实际项目中要不断调整和优化,有时候线程多了效率还会降低。
  • 因为主线程是自动启动的,所以一个进程中最少也是有一个线程(主线程)的。所以进程与线程有点像父亲和孩子的关系。
  • 多线程程序可以同时做多件事,所以运行效率更高。但到底有多高,并不是一个很容易评估和量化的事情,仍旧需要在实际编程与实际项目中体会和调整。

很多人对于多线程开发觉得复杂、难用、不好控制。其实,多线程是一种非常强大的工具,能把多线程程序写好,写高效和稳定,不但是作为一个高级开发人员实力的体现,也是很实际的项目必须采用的一种开发方式,因为只有这样,程序的运行效率才能满足实际生产境的需要(就像上面举的例子,不可能让玩家2白白地等10s)。换句话说,很多场合下,必须用多线程开发技术,所开发的程序才有实际的商用价值。

并发的实现方法

这里还是要总结一下,把并发、进程、线程概念往一起串一串。

怎样实现并发呢?以下两种实现手段都可以:

  • 通过多个进程来实现并发,每个进程做一件事。这里所说的进程,指的是这种只包含一个主线程的进程,这种手段并不需要在程序代码中书写任何与线程有关的代码。
  • 在单独的一个进程中创建多个线程来实现并发,这种情况下就得书写代码来创建除主线程外的其他线程了(主线程不需要创建,进程一启动,主线程自动就存在并开始运行了)。

多进程并发

看一看多进程并发。执行一个可执行程序就生成了一个进程,如果思路拓展一点,想一想,例如启动了一个Word(Office办公软件之一)用来打字,就是一个Word进程:启动测览器观看一个网页,就是一个浏览器进程。

回归到项目中来,例如写一个网络游戏,有账号服务器、游戏逻辑服务器,这每一个都是一个可执行程序,把它们运行起来,每个服务器就是一个进程,这些进程之间可能也需要互相通信,例如账号服务器要把账号信息发送给游戏逻辑服务器用来进行身份验证。进程之间的通信手段比较多,如果是同一台计算机上的进程之间的通信,可以使用管道、文件、消息队列、共享内存等技术来实现,而在不同的计算机之间的进程通信可以使用socket(网络套接字)等网络通信技术来实现。由于进程之间数据保护问题,即便是在同一个计算机上,进程之间的通信也是挺复杂的。

多线程并发

多线程就是在单个的进程中创建多个线程。所以,线程有点类似于轻量级的进程,每个线程都是独立运行的,但是一个进程中的所有线程共享地址空间(共享内存),还有诸如全局变量、指针、引用等,都是可以在线程之间传递的,所以可以得出一个结论:使用多线程的开销远远小于多进程。

当然,多线程使用共享内存虽然灵活,但是也带来了新的问题——数据一致性问题。例如,线程A要写一块数据,同时线程B也要写这块数据,那么,就需要采取一定的技术手段,让它们有先有后地去写,而不能同时去写,如果同时去写,可能写进去的数据就会出现互相覆盖等数据不一致的错误。

虽然多进程并发和多线程并发可以混合使用,但是一般来讲,建议优先考虑使用多线程的技术手段来实现并发而不是多进程。在本章中也只讲解多线程的并发相关的技术。所以,后续谈到并发,指的都是多线程并发。

总结

和多进程并发比较来讲,多线程并发的优缺点如下。

优点:线程启动速度更快,更轻量级;系统资源开销更少;执行速度更快。

缺点;使用起来有一定难度,要小心处理数据的一致性问题。

C++11新标准线程库

以往要写多线程的程序,每个不同的操作系统平台都有不同的线程创建方法,如Windows操作系统下,用CreateThread函数创建线程,并且还有一堆和线程相关的其他函数和概念,如临界区、互斥量等。 Linux 操作系统下也一样,如用 pthread_create 创建线程。

所以可以看到,这些代码都不一样,不能跨操作系统(跨平台)使用。当然,如果使用一些跨平台的多线程库如POSIX thread(pthread)是可以的,这样就可以在不同的操作系统平台上写相同的多线程相关程序代码。但是,为了支持pthread,在Windows操作系统下要配置一番,如果换成Linux操作系统,也要在Linux操作系统下配置一番,这两个不同的操作系统平台,配置方法多多少少会有不同,所以pthread使用起来也并不是那么方便。

现在,好消息来了,从C++11新标准开始,C++语言本身增加了针对多线程的支持。这意味着可以使用C++语言本身提供的编程接口(方法)来编写和具体的操作系统平台无关的多线程程序,极大地增加了程序的可移植性,在Windows下开发的C++多线程程序代码可以不用修改源代码,直接拿到同样支持C++11新标准的Linux平台的C++编译器上编译(这就是跨平台)。在实际的开发中,如果要求必须实现跨平台开发时,这会大量地减少开发人员的工作量,实在是很好。

代码地址

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

html 3D立体多形态旋转音乐相册 | 2022都结束了,还不快给女神制作一个特殊的纪念相册

📋 前言 🖱 博客主页:在下马农的碎碎念✍ 本文由在下马农原创,首发于CSDN📆 首发时间:2023/01/07📅 最近更新时间:2023/01/07🤵 此马非凡马,房星本是星。向前…

零基础掌握IP地址知识,小白必学知识点!

前言 大家好,在生活中我们使用具有上网功能的电子设备都有IP地址,就跟每个人都有自己的名字一样。IP地址分为IPV4 IPV6,我们所说的的IP地址指的是IPV4的地址。 正文 IPV4( Internet Protocol Version 4 )互联协议版本4,有版本V4之…

【HTML+CSS+JavaScript】动感爱心—— I love you~

有段时间没有分享了,no time。 还是抽出一会儿分享一下。有时间会解析的(具体…I don’t know)。 1. 效果展示 真实效果挺好看的,喜欢的朋友,可以给你的女朋友或者喜欢的TA看看呀! 可以根据实际情况修改文案,比如诗歌,或者你爱的人的名字哦~ 2. 源代码分享 2.1 动感…

在Ubuntu上安装docker(Ubuntu版本18.04)

在Ubuntu上安装docker详细步骤1、卸载之前的docker版本2、安装docker仓库3、在系统中添加Docker的官方密钥4、添加docker源5、再次更新源列表6、查看可以安装的docker版本并安装docker7、使用命令查看是否安装成功以及安装的docker版本8、启动 docker服务并设置开机自动启动doc…

FPGA基础之modelsim常见问题

目录 问题一:modelsim破解失败 1)现象 2)原因 ​ 3) 解决 问题一:modelsim破解失败 1)现象 modelsim激活失败,原先正常使用过的,重新卸载安装破解,设置环境变量…

Serverless 奇点已来,下一个十年将驶向何方?

本文整理自 QCon 上海站 2022 丁宇(叔同)的演讲内容。 以前构建应用,需要买 ECS 实例,搭建开源软件体系然后维护它,流量大了扩容,流量小了缩容,整个过程非常复杂繁琐。 用了 Serverless 服务以…

【如何添加本地jar包到maven依赖】

如何添加本地jar包到maven依赖 1、本地jar包和对应依赖如下图(刚开始这俩依赖是报红的) 2、执行mvn命令如下: mvn install:install-file -DfileD:\ht_mesis-platform\mesis-business\dandian4-1.0.0.jar -Dpackagingjar -DgroupIddandian4…

Java实战:Hutool类库中的DateUtil用法总结

❤️作者主页:IT技术分享社区 ❤️作者简介:大家好,我是IT技术分享社区的博主,从事C#、Java开发九年,对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️荣誉: CSDN博客专家、数据库优质创作者🏆&…

微信小程序 | 一比一复刻抖音短视频

📌个人主页:个人主页 ​🧀 推荐专栏:小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏!从个人到商业的全套开发教程,实打实的干货分享,确定不来看看? …

Java中常用API总结(6)——BigInteger类

BigInteger类一、前言二、概述1.API帮助文档2.使用方法三、常见方法1.构造方法1️⃣格式2️⃣实例2.成员方法1️⃣格式2️⃣实例四、注意事项五、结语一、前言 平时在存储整数的时候,Java中默认是int类型,int类型有取值范围:-2147483648 ~ 2…

【深入理解JVM】内存模型

目录 运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 Java堆 方法区 运行时常量池 直接内存 虚拟机对象探秘 对象的创建 对象的内存布局 运行时数据区域 程序计数器 程序计数器是一块较小的内存空间,存储当前线程所执行的字节码指令的地址。在java…

C#开发的资源文件程序(可国际化) - 开源研究系列文章

上次将小软件的线程池描述了,也将插件程序描述了,这次就将里面的资源文件相关的内容进行下记录,这里能够让程序做成国际化的形式(即多语言程序),主要就是通过这个资源文件的方式进行的处理。下面将对这个资源文件的定义进行描述&a…

多线程之waitnotify

目录: 前言 1.wait()方法 2 notify()方法 3.wait & notify的代码示例: 4.关于notifyAll()方法 前言 线程最大的问题就是抢占式执行,随机调度。虽然线程在操作系统内核里的调度是随机的,但是可以通过一些办法来控制线程…

带你了解SVG标签

✍️ 作者简介: 前端新手学习中。 💂 作者主页: 在主页中查看更多前端教学,可接大学生前端作业单。 🎓 专栏分享:css重难点教学 Node.js教学 从头开始学习 ajax学习 js学习 目录初始SVG矩形,圆形和椭圆型 矩形 圆…

云安全(云安全数据中心、WAF、DDOS)

安全 安全威胁 可用性 安全威胁:大规模分布式拒绝服务攻击(DDoS)、僵尸网络(Botnet) 影响:网站业务不可用 完整性 安全威胁:网站入侵、服务器口令暴力破解 影响:网站页面被篡改和植入后门 保密性 安全威胁:网站后门…

二、Groovy入门

文章目录二、Groovy入门2.1 Groovy 简介2.2 Groovy 安装[非必须]2.3 IDEA创建 Groovy 项目2.4 Groovy 基本语法2.4.1 案例 1:基本注意点2.4.2 案例 2:引号说明2.4.3 案例 3:三个语句结构2.4.4 案例 4:类型及权限修饰符2.4.5 案例 5:集合操作2.4.6 案例 6:类导入2.4.7 案例 7:异…

MySQL是怎么保证主备一致的?

在前面的文章中,我不止一次地和你提到了 binlog,大家知道 binlog 可以用来归档,也可以用来做主备同步,但它的内容是什么样的呢?为什么备库执行了 binlog 就可以跟主库保持一致了呢?今天我就正式地和你介绍一下它。 毫不夸张地说,MySQL 能够成为现下最流行的开源数据库,…

pytorch torchvision.ops.roi_align

pytorch的torchvision.ops.roi_align这个算子真的是坑我好多天啊!害我连续加班半个月!二阶段目标检测后面用roi_align来提取特征。 接口官方说明:https://pytorch.org/vision/stable/generated/torchvision.ops.roi_align.html?highlightroi…

React Devtools 使用技巧

首先在扩展迷中搜索下载该扩展,引入到 Chrome 的扩展程序中。 当我们添加扩展到Chrome中,就会在浏览器中看到 React Devtools 的 Icon,同时通过该扩展我们就可以知道当前打开的网站是开发环境的网站还是生产环境,React Devtools …

Mysql 安装 ubutu20.04

Mysql 安装 1:sudo apt-get autoremove --purge mysql* 2:sudo apt-get install mysql-server 3:sudo apt --fix-broken install -y 4:sudo apt-get install mysql-server 5: service mysql status 5:sudo…