【面试题】创建两个线程交替打印100以内数字(一个打印偶数一个打印奇数)
阅读导航
- 一、问题概述
- 二、解决思路
- 三、代码实现
- 四、代码优化
一、问题概述
面试官:C++多线程了解吗?你给我写一下,起两个线程交替打印0~100的奇偶数。就是有两个线程,一个线程打印奇数另一个打印偶数,它们交替输出,类似这样。
偶线程:0
奇线程:1
偶线程:2
奇线程:3……
偶线程:98
奇线程:99
偶线程:100
面对突如其来的面试题,确实可能会让人感到手足无措。即便你已经掌握了多线程的相关知识,面试官突然提出一个问题,短时间内想要构思出一个解决方案可能还是有些困难。实际上,这类问题所涉及的知识点通常并不复杂,但如果在准备面试时没有遇到过类似的题目,想要迅速想出解决方案确实需要一定的技巧,而且面试官往往还要求面试者现场手写代码。
二、解决思路
回到题目本身,我们需要处理的是两个线程的协作问题,并且要求它们能够交替打印数字。这涉及到线程间的通信和同步。在这种情况下,我们可以想到的基本策略是使用锁来控制线程的执行顺序。拿到锁的线程可以执行打印操作,然后释放锁,让另一个线程有机会获取锁。这样,两个线程就可以轮流获得锁,实现交替打印的效果。
创建两个线程并不复杂,实现加锁机制也相对简单。关键在于如何确保这两个线程能够公平地轮流获取锁。我们知道,在加锁之后,线程之间会相互竞争以获取锁。C++标准库中的锁默认并不保证公平性(也就是说,不能保证先请求锁的线程一定会先获得锁),这就可能导致一个线程连续打印多次,而另一个线程则长时间无法打印。
为了解决这个问题,我们可以设计一种机制来确保两个线程能够轮流打印。例如,我们可以定义一个全局变量来指示哪个线程应该先打印,然后每个线程在尝试获取锁之前先检查这个全局变量,确保只有当它应该打印时才去竞争锁。这样,我们就可以避免一个线程长时间占用锁,从而实现两个线程的公平交替打印。
三、代码实现
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>int main()
{// 创建互斥锁用于同步线程std::mutex mtx;// 初始化全局变量x为1,代表要打印的第一个数字int x = 1;// 创建条件变量用于线程间同步std::condition_variable cv;// 标志变量,用于控制哪个线程应该执行bool flag = false;// 创建线程t1,负责打印奇数std::thread t1([&]() {for (size_t i = 0; i < 50; i++){// 锁定互斥锁std::unique_lock<std::mutex> lock(mtx);// 如果flag为true,则等待cv的通知while (flag)cv.wait(lock);// 打印当前线程ID和x的值std::cout << "奇线程: " << x << std::endl;// x加1,准备打印下一个数字++x;// 将flag设置为true,允许t2执行flag = true;// 通知一个等待cv的线程cv.notify_one(); }});// 创建线程t2,负责打印偶数std::thread t2([&]() {for (size_t i = 0; i < 50; i++){// 锁定互斥锁std::unique_lock<std::mutex> lock(mtx);// 如果flag为false,则等待cv的通知while(!flag)cv.wait(lock);// 打印当前线程ID和x的值std::cout << "偶线程: " << x << std::endl;// x加1,准备打印下一个数字++x;// 将flag设置为false,允许t1执行flag = false;// 通知一个等待cv的线程cv.notify_one();}});// 等待线程t1和t2完成t1.join();t2.join();// 程序正常退出return 0;
}
上面的这段代码让两个线程交替打印奇数和偶数。下面是代码实现的核心思路:
-
初始化同步工具:
std::mutex mtx;
:创建一个互斥锁mtx
,用于保护共享资源(在这个例子中是变量x
和flag
)的访问。std::condition_variable cv;
:创建一个条件变量cv
,用于线程间的同步和通信。bool flag = false;
:创建一个标志变量flag
,用于控制线程t1
和t2
的执行顺序。
-
创建线程:
- 使用
std::thread
创建两个线程t1
和t2
,它们将共享相同的函数对象,但执行不同的任务。
- 使用
-
线程t1的逻辑:
t1
负责打印奇数。- 使用
std::unique_lock
锁定互斥锁mtx
,确保对共享资源的安全访问。 - 通过
while (flag)
循环和cv.wait(lock)
调用,t1
在flag
为true
时等待,这是为了让t2
先执行。 - 当
flag
为false
(即t2
执行完毕后),t1
打印当前的x
值,然后将x
加1。 - 将
flag
设置为true
,表示t1
已经执行完毕,现在轮到t2
执行。 - 调用
cv.notify_one()
唤醒等待在cv
上的一个线程,即t2
。
-
线程t2的逻辑:
t2
负责打印偶数。- 类似于
t1
,t2
首先锁定互斥锁mtx
。 - 通过
while(!flag)
循环和cv.wait(lock)
调用,t2
在flag
为false
时等待,这是为了让t1
先执行。 - 当
flag
为true
(即t1
执行完毕后),t2
打印当前的x
值,然后将x
加1。 - 将
flag
设置为false
,表示t2
已经执行完毕,现在轮到t1
执行。 - 调用
cv.notify_one()
唤醒等待在cv
上的一个线程,即t1
。
-
等待线程结束:
- 使用
t1.join()
和t2.join()
确保主线程等待t1
和t2
线程完成执行。
- 使用
-
程序退出:
return 0;
表示程序正常退出。
这种使用互斥锁、条件变量和标志变量的模式是多线程同步中常见的一种方法,它允许多个线程以一种协调的方式交替执行任务。通过这种方式,可以避免竞态条件和数据不一致的问题,确保线程安全。
四、代码优化
代码可以进行一些优化以提高其可读性和效率。
-
使用
std::atomic
:
使用std::atomic<int>
代替int
类型来声明x
,这样可以避免在多线程环境中对x
的访问需要互斥锁的保护。 -
减少锁的范围:
缩小互斥锁的使用范围,只在必要时锁定和解锁,以减少锁的争用。 -
使用
std::chrono
:
使用std::chrono
库中的类型来指定condition_variable
的超时时间,以避免长时间等待。 -
使用
notify_all
代替notify_one
:
如果只有两个线程在等待同一个条件变量,使用notify_all
可以避免唤醒一个线程后再次等待。 -
代码重构:
将线程函数提取为独立的函数,以提高代码的可读性和可维护性。
下面是优化后的代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <chrono>std::mutex mtx;
std::condition_variable cv;
std::atomic<int> x(1); // 使用原子操作来保证线程安全
bool flag = false;void print_numbers(bool is_odd) {for (size_t i = 0; i < 50; i++) {std::unique_lock<std::mutex> lock(mtx);while (flag != is_odd) {cv.wait(lock, []{ return flag != is_odd; }); // 使用lambda表达式指定唤醒条件}std::cout << std::this_thread::get_id() << ":" << x++ << std::endl;flag = !is_odd; // 切换flag的值cv.notify_all(); // 唤醒另一个线程}
}int main() {std::thread t1(print_numbers, true);std::thread t2(print_numbers, false);t1.join();t2.join();return 0;
}
在这个优化版本中:
x
被声明为std::atomic<int>
类型,因此不需要互斥锁来保护x
的增加操作。- 条件变量的等待条件被封装在lambda表达式中,这样可以更清晰地指定唤醒条件。
- 使用
notify_all()
来唤醒所有等待的线程,因为在这个场景中只有两个线程,所以notify_one()
和notify_all()
效果相同,但notify_all()
是一个更通用的选择。 - 将打印逻辑抽象到
print_numbers
函数中,并使用is_odd
参数来区分是打印奇数还是偶数。
相关文章:
【面试题】创建两个线程交替打印100以内数字(一个打印偶数一个打印奇数)
阅读导航 一、问题概述二、解决思路三、代码实现四、代码优化 一、问题概述 面试官:C多线程了解吗?你给我写一下,起两个线程交替打印0~100的奇偶数。就是有两个线程,一个线程打印奇数另一个打印偶数,它们交替输出&…...
PgMP考试结束后多久出成绩?附成绩查询方法
PgMP考试结束后多久出成绩?这是许多参加PgMP考试的考生都非常关心的问题。今天就给大家讲解一下PgMP考试多久可以知道成绩? 一、PgMP考试成绩查询时间 PgMP考试一般在考试结束后的6-8周左右才会出成绩,届时PMI官方会通过电子邮件的形式提醒…...
springboot项目Redis统计在线用户
springboot项目Redis统计在线用户 我的项目有个显示用户的遗忘曲线,需要统计在线用户以计算他们的曲线 思考了两种方案,但都是用Redis的bitmap数据结构Bitmap是一种特殊类型的数组,其中每个元素只能存储0或1。在Redis中,Bitmap实际…...
GNeRF论文理解
文章目录 主要解决什么问题?结构设计以及为什么有效果?个人想法。 主要解决什么问题? 本文主要想要解决的问题是 如何使用uncalibrated的照片来进行Nerf重建。虽然说现在已经有了一些方式可以对相机位姿进行估计和优化,但是他们限…...
0531作业 链表
结果 整体代码 主要实现 /**实现* */ #include "./linklist.h"linklist* create_linklist(datatype param){linklist* node(linklist*)malloc(sizeof(linklist));if(NULLnode){puts("节点创建失败");}node->paramparam;node->pnextNULL;puts("…...
C++ STL - 容器
C STL(标准模板库)中的容器是一组通用的、可复用的数据结构,用于存储和管理不同类型的数据。 目录 零. 简介: 一 . vector(动态数组) 二. list(双向链表) 三. deque(…...
AI生成沉浸式3D世界(空间照片/视频)
面向Vision Pro等空间计算设备的产品指南 & 技术实现路径 一、通俗理解 ldi3格式概览:这是一种创新的3D内容格式,专为Vision Pro、Quest等VR头戴设备设计,让你能沉浸在一个几可乱真的三维世界,体验仿佛亲临其境的感受。 内容创作:利用开源工具,结合多角度摄像捕捉,…...
【Vue】异步更新 $nextTick
文章目录 一、引出问题二、解决方案三、代码实现 一、引出问题 需求 编辑标题, 编辑框自动聚焦 点击编辑,显示编辑框让编辑框,立刻获取焦点 即下图上面结构隐藏,下面结构显示,并且显示的时候让它自动聚焦。 代码如下 问题 “…...
【uCOS-III-编程指南】
uCOS-III-编程指南 ■ [野火]uCOS-III内核实现与应用开发实战指南■■■■ ■ [野火]uCOS-III内核实现与应用开发实战指南 添加链接描述 ■ ■ ■ ■...
2004NOIP普及组真题 2. 花生采摘
线上OJ: 【04NOIP普及组】花生采摘 核心思想: 1、本题为贪心即可。 2、因为本题严格限制了顺序,所以先把每个节点的花生数量按降序排序。然后逐一判断下一个花生是否需要去采摘即可 3、每一次采摘完,记录耗时 t 以及采集的花…...
SAP-SD-21-定义用于定价补充的定价过程
图9 维护条件类型...
Android AAudio——C API创建AudioTrack(六)
虽然 AAudio 试图提供一种直接的硬件访问途径,但在某些场景下,如处理兼容性问题、使用系统服务(如 AudioFlinger)或者在某些设备上,使用 AudioTrack 可能是最有效或最合适的途径。这并不违背 AAudio 的初衷,因为它的目标是提供高性能的音频处理,而不是避免使用系统服务。…...
实验七、创建小型实验拓扑《计算机网络》
早检到底是谁发明出来的。 一、实验目的 完成本实验后,您将能够: • 设计逻辑网络。 • 配置物理实验拓扑。 • 配置 LAN 逻辑拓扑。 • 验证 LAN 连通性。 二、实验任务 在本实验中,将要求您连接网络设备并配置主机实现基本的网络…...
SqlServer2016企业版安装
前言 好久没有知识的累积,最近工作上遇到新的SqlServer2016安装,记录一下 参考文章 SQL Server 2016软件安装包和安装教程 - 哔哩哔哩 (bilibili.com) 安装包准备 需要提前准备软件安装包如下 cn_sql_server_2016_enterprise_x64_dvd_8699450&…...
HBase数据库面试知识点:第一部分 - 基础概念与特点(持续更新中)
目录 一、HBase基础概念 1. HBase定义 2. 核心组件 3. HBase的特点 二、HBase与传统RDBMS的区别 1. 数据类型 2. 数据操作 3. 存储方式 4. 伸缩性 5. 事务性 三、HBase数据模型 四、HBase的特点 五、HBase与Hadoop生态系统的关系 一、HBase基础概念 1. HBase定义 …...
一个高效的go语言字符串转驼峰命名算法实现函数
在go语言的开发中我们经常需要对各种命名进行规范, 今天给大家介绍的是一个高效的将字符串转 驼峰命名 (即 首字母大写的命名方式)的函数。 // 字符串转驼峰命名 // author tekintian <tekintiangmail.com> func CamelStr(str string) …...
Python中__init__方法的魔力:构建对象的基石
Python中__init__方法的魔力:构建对象的基石 在Python的世界中,__init__方法是一个特殊的存在。它不仅是类的构造函数,更是对象生命周期的起点。通过__init__方法,我们可以初始化对象的状态,设置属性,甚至…...
Appium安装及配置(Windows环境)
在做app相关自动化测试,需要使用appium来做中转操作,下面来介绍一下appium的环境安装配置 appium官方文档:欢迎 - Appium Documentation 一、下载appium 下载地址:https://github.com/appium/appium-desktop/releases?page3 通…...
CANOE制造dll文件,以及应用dll文件
1、使用canoe自带的capl dll 2、然后使用Visual Studio 2022 打开项目 3、项目打开后修改下项目属性 4、修改capldll.cpp文件 4.1 添加的内容 void CAPLEXPORT far CAPLPASCAL appSum(long i, long j, long* s){*s i j;} {"sum", (CAPL_FARCALL)appSum, "…...
C++结合OpenCV进行图像处理与分类
⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生👨🎓。 如果觉得本文能帮到您,麻烦点个赞👍呗! 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三…...
Master-Worker 架构的灰度发布难题
作者:石超 一、前言 Master-Worker 架构是成熟的分布式系统设计模式,具有集中控制、资源利用率高、容错简单等优点。我们数据中心内的几乎所有分布式系统都采用了这样的架构。  我们曾经发生过级联故障,造成了整个集群范围的服…...
钢基础知识介绍
钢铁是一种铁碳合金,含有一定量的碳和其他合金元素,如硅、锰等。而钢材则是经过加工处理后的钢铁材料,具有更高的强度、硬度、塑性和韧性。钢铁的硬度、强度和耐磨性相对较低,而钢材经过加工处理后,其硬度、强度和耐磨…...
linux 系统监控脚本
1.对CPU的监控函数 function GetCpu(){cpu_numgrep -c "model name" /proc/cpuinfocpu_usertop -b -n 1 | grep Cpu | awk {print $2} | cut -f 1 -d "%"cpu_systemtop -b -n 1 | grep Cpu | awk {print $4} | cut -f 1 -d "%"cpu_idletop -b -…...
K8s Pod的QoS类
文章目录 OverviewPod的QoS分类Guaranteed1.如何将 Pod 设置为保证Guaranteed2. Kubernetes 调度器如何管理Guaranteed类的Pod Burstable1. 如何将 Pod 设置为Burstable2.b. Kubernetes 调度程序如何管理 Burstable Pod BestEffort1. 如何将 Pod 设置为 BestEffort2. Kubernete…...
TCP/IP协议栈
一、TCP/IP协议栈和OSI参考模型对比 二、TCP/IP五层功能 三、TCP/IP模型的层间通信与数据封装 四、TCP/IP模型的层间通信与数据解封装...
Vector容器详解
Vector容器详解 本文将详细介绍C#中的Vector容器,包括其定义、特点、使用方法以及示例代码。 目录 Vector容器简介Vector容器的特点Vector容器的使用方法示例代码 1. Vector容器简介 Vector容器是一种动态数组,它可以自动调整大小以容纳更多的元素。…...
设计模式-抽象工厂(创建型)
创建型-抽象工厂 角色 抽象工厂: 声明创建一个族产品对象的方法,每个方法对应一中产品,抽象工厂可以是接口,也可以是抽象类;具体工厂: 实现抽象工厂接口,复杂创建具体的一族产品;抽…...
攻防世界---web---Web_php_unserialize
1、题目描述 2、 3、分析代码 class Demo { private $file fl4g.php; }:定义了一个名为Demo的类,该类有一个私有属性$file,默认值为fl4g.php。 $a serialize(new Demo);:创建了一个Demo类的实例,并对其进行序列化&a…...
嵌入式学习记录
一 环境搭建 1.Ubuntu ssh登陆开发板,短命令替换ssh命令 交叉编译命令 sudo gedit ~/.bashrc # 文件结尾加入: alias tob"ssh root192.168.1.104" alias gb"arm-buildroot-linux-gnueabihf-gcc"往后终端输入top 相当于输入ssh roo…...
使用from…import语句导入模块
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在使用import语句导入模块时,每执行一条import语句都会创建一个新的命名空间(namespace),并且在该命名…...
做外贸可以在哪些网站注册/网络科技公司经营范围
原标题:叮!请查收这份计算机二级考试攻略!君君掐指一算距离计算机二级考试只剩十多天了不知道各位报过名的同学们你们都准备好迎接考试了吗来一起随君君get计算机二级的考试攻略吧计算机二级证书的重要性很多同学都有些犹豫到底要不要考计算机…...
农村基本制度建设网站/整站优化 快速排名
一.获取原生DOM的方式 给标签或者属性添加ref属性 1 //1.添加属性 2 <div refshy><div> 3 <Home refhome></Home> 4 //2.获取原始DOM 5 this.$refs.shy 6 //3.组件实例化对象 7 this.$refs.home 使用实例 1 <body>2 <div id"app"&g…...
网站建设超链接字体变色代码/百度推广授权代理商
导读:给各位介绍七个自动机器学习框架,希望有价值。这些年,机器学习(Machine Learning)的使用率越来越高,模型给企业带来一系列机会,也给未来留下更好的畅想。但是,机器学习的建模流程时间长且复杂…...
网络广告营销的实现方式/seo具体优化流程
我们要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码(这里“代码”对应整个应用的第三部分:一系列在接收到请求时真正工作的处理程序)。 因此,我们需要查看HTTP请求&#…...
做生鲜的网站/semseo
西安邮电大学计算机学院本科论文西 安 邮 电 大 学毕 业 设 计(论 文)题 目: JSP用户注册登录系统设计——重设密码模块学 院: 计算机学院专 业: 计算机科学与技术班 级: 计科1101班学生姓名: 廖建军导师姓名ÿ…...
上海通信管理局网站/网络营销推广活动有哪些
本文主要讲述 React 的诞生过程和优化思路。 内容整理自 2014 年的 OSCON - React Architecture by vjeux,虽然从今天(2018)来看可能会有点历史感,但仍然值得学习了解。以史为鉴,从中也可以管窥 Facebook 优秀的工程管…...