【面试题】创建两个线程交替打印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进行图像处理与分类
⭐️我叫忆_恒心,一名喜欢书写博客的在读研究生👨🎓。 如果觉得本文能帮到您,麻烦点个赞👍呗! 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧,喜欢的小伙伴给个三…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
