文章目录

  • 86. C++多线程中,锁的实现方式有哪些?
    • 1. 互斥锁(Mutex)
    • 2. 递归互斥锁(Recursive Mutex)
    • 3. 读写锁(Shared Mutex)
    • 4. 自旋锁(Spinlock)
    • 5. 条件变量(Condition Variable)
  • 87. 内存对齐是什么,为什么要做内存对齐
  • 88. 结构体和联合体的区别?
  • 89. struct和class的区别?
  • 90. 为什么TCP握手是3次,不能是2次和4次吗?

86. C++多线程中,锁的实现方式有哪些?

在这里插入图片描述

1. 互斥锁(Mutex)

互斥锁(std::mutex)是最常见的同步机制,用于保护临界区,使得同一时刻只有一个线程可以访问共享资源。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;void print_safe(const std::string& msg) {std::lock_guard<std::mutex> guard(mtx); // 自动加锁和解锁std::cout << msg << std::endl;
}int main() {std::thread t1(print_safe, "Hello from thread 1");std::thread t2(print_safe, "Hello from thread 2");t1.join();t2.join();return 0;
}

在这里插入图片描述

  • std::lock_guard:常用于在作用域范围内自动管理锁,确保即使发生异常,锁也会被正确释放。

2. 递归互斥锁(Recursive Mutex)

递归互斥锁(std::recursive_mutex)允许同一个线程多次获得同一把锁,而不会引起死锁。适用于递归函数或需要多次加锁的场景。

#include <iostream>
#include <thread>
#include <mutex>std::recursive_mutex rec_mtx;void recursive_function(int n) {if (n <= 0) return;std::lock_guard<std::recursive_mutex> guard(rec_mtx);std::cout << "Recursion depth: " << n << std::endl;recursive_function(n - 1);
}int main() {std::thread t1(recursive_function, 5);t1.join();return 0;
}

在这里插入图片描述

3. 读写锁(Shared Mutex)

读写锁(std::shared_mutex in C++17)允许多个线程同时读取数据,但在写数据时,只有一个线程可以获得写锁。适用于读操作频繁、写操作较少的场景

#include <iostream>
#include <thread>
#include <shared_mutex>
#include <mutex>std::shared_mutex rw_mtx;
int shared_data = 0;void read_data() {std::shared_lock<std::shared_mutex> lock(rw_mtx);std::cout << "Reading data: " << shared_data << std::endl;
}void write_data(int value) {std::unique_lock<std::shared_mutex> lock(rw_mtx);shared_data = value;std::cout << "Writing data: " << shared_data << std::endl;
}int main() {std::thread t1(read_data);std::thread t2(write_data, 42);t1.join();t2.join();return 0;
}

在这里插入图片描述

4. 自旋锁(Spinlock)

自旋锁是一种忙等待的锁实现,线程在等待锁的过程中会不断检查锁的状态,而不是进入休眠。适用于锁等待时间非常短的场景。

在这里插入图片描述

#include <atomic>
#include <thread>class Spinlock {std::atomic_flag flag = ATOMIC_FLAG_INIT;public:
/*
atomic_flag.test_and_set(acquire):把标志设为 true,返回设之前的值。
返回 false → 说明之前没人持锁 → 你拿到锁。
返回 true → 已经有人持锁 → 你就在 while 里原地打转(自旋)等别人放锁。
atomic_flag.clear(release):把标志清为 false,表示释放锁。
*/void lock() {// 返回true就一直转圈等// Acquire(获取):我看见门开了才进去拿资料。// ⇒ 我进门这刻(acquire)之后要读的东西,不允许被提前到进门之前去读;进去后能看见别人关门前放好的资料。// test_and_set(acquire):确保拿到锁后再读写共享数据,后续读写不会被提前到拿锁之前。while (flag.test_and_set(std::memory_order_acquire)) {// 自旋等待,什么也不做}}void unlock() {// 放锁// Release(释放):我把活儿干完、把资料都装进箱子,再把“门”关上发出通知。// ⇒ 门关上这刻(release)之前我做的所有修改,都必须先完成,不能被拖到门关之后。// clear(release):确保临界区里对共享数据的修改,在清锁之前完成并对外可见。flag.clear(std::memory_order_release);}
};Spinlock spinlock;void critical_section() {spinlock.lock();// 访问共享资源spinlock.unlock();
}int main() {std::thread t1(critical_section);std::thread t2(critical_section);t1.join();t2.join();return 0;
}
#include <atomic>
#include <thread>
#include <iostream>
#include <chrono>// 把下面这个宏改成 1 可以切到 relaxed 版本做对比
// USE_RELAXED=0(默认):ready.store(..., memory_order_release) + ready.load(..., memory_order_acquire)
// ——正确的发布/获取配对。// USE_RELAXED=1:把两边都改成 memory_order_relaxed
// ——没有跨线程的可见性保证。
#ifndef USE_RELAXED
#define USE_RELAXED 0
#endifint data = 0;                 // 普通共享数据(非原子)
std::atomic<bool> ready{false}; // 原子标志:告诉对方“数据准备好了”void producer() {data = 42; // 普通写:把共享数据准备好#if USE_RELAXED// 没有发布语义:可能发生重排,消费者即便看到 ready=true,也未必能看到 data=42ready.store(true, std::memory_order_relaxed);
#else// 发布(release):保证 data 的写先于 ready=true 对外可见// 发布:把之前对 data 的写先“封口发布”ready.store(true, std::memory_order_release);
#endif
}int consumer(int& seen) {
#if USE_RELAXEDwhile (!ready.load(std::memory_order_relaxed)) {// 自旋等待std::this_thread::yield();}
#else// 获取(acquire):保证看到 ready=true 后,再读 data 时能看到发布方的修改while (!ready.load(std::memory_order_acquire)) {// std::this_thread::yield(); 是 C++11 提供的线程调度提示(在 <thread> 里)。作用是:把当前线程剩余的时间片“让出来”,告诉操作系统调度器“我现在没啥可做了,可以先让别的就绪线程跑一会儿”。函数返回 void,不抛异常。std::this_thread::yield();          // 让出时间片,避免满负荷空转}
#endifint x = data;   // 普通读seen = x;return x == 42 ? 0 : 1; // 返回是否出错
}int main() {// 跑多轮,观察是否有不一致const int rounds = 100000;  // 可按机器性能调整int failures = 0;for (int i = 0; i < rounds; ++i) {// 每轮复位data = 0;ready.store(false, std::memory_order_relaxed);int seen = -1;std::thread tC([&]{ failures += consumer(seen); });std::thread tP(producer);tP.join();tC.join();// 可选:偶尔打印下现场(避免刷屏)if (i % 25000 == 0) {
#if USE_RELAXEDstd::cout << "[relaxed] round " << i << ", seen=" << seen << "\n";
#elsestd::cout << "[acq/rel] round " << i << ", seen=" << seen << "\n";
#endif}}#if USE_RELAXEDstd::cout << "[relaxed] failures = " << failures<< " / " << rounds << " (未必容易复现,但理论上可能出错)\n";
#elsestd::cout << "[acquire/release] failures = " << failures<< " / " << rounds << " (应始终为 0)\n";
#endifreturn 0;
}

在这里插入图片描述

5. 条件变量(Condition Variable)

条件变量(std::condition_variable)并不单独作为锁,而是与互斥锁结合使用。它允许线程在等待某个条件满足时被阻塞,并在条件满足时被唤醒。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void worker() {std::unique_lock<std::mutex> lock(mtx);// lock_guard 没有 unlock()/lock() 接口,生命周期内“始终持有”是它的契约;而 unique_lock 提供了这些成员函数并允许临时失去所有权// lock_guard 的设计是极简 RAII:构造即上锁、析构即解锁,中间不允许变更持锁状态,也不可移动。// unique_lock 的设计是“可拥有/可转移/可显式 lock/unlock/release”,契合 wait 在等待期间修改持锁状态的需求。// 这是 条件变量的“带谓词等待” 用法。// cv.wait(lock, [] { return ready; }); 的意思是:在持有 lock(一个 std::unique_lock<std::mutex>)的前提下,一直等到 ready 为 true 才返回;否则就阻塞等待通知。cv.wait(lock, [] { return ready; }); // 等待条件变量std::cout << "Worker thread is running..." << std::endl;
}void signal() {std::lock_guard<std::mutex> lock(mtx);ready = true;std::cout << "signal thread is running..." << std::endl;cv.notify_one(); // 唤醒等待中的线程
}int main() {std::thread t1(worker);std::thread t2(signal);t1.join();t2.join();return 0;
}

在这里插入图片描述

87. 内存对齐是什么,为什么要做内存对齐

在这里插入图片描述
在这里插入图片描述

88. 结构体和联合体的区别?

在这里插入图片描述

#include <iostream>
#include <cstring>  // 用于使用 strcpyunion Data {int i;float f;char str[20];
};int main() {Data data;  // 创建一个联合体变量// 使用 int 成员data.i = 42;std::cout << "data.i: " << data.i << std::endl;// 使用 float 成员data.f = 3.14;std::cout << "data.f: " << data.f << std::endl;// 使用字符串成员strcpy(data.str, "Hello");std::cout << "data.str: " << data.str << std::endl;// 观察内存重用的效果,可能会出现未定义的 乱七八糟的内容std::cout << "After setting data.str, data.i: " << data.i << std::endl;std::cout << "After setting data.str, data.f: " << data.f << std::endl;return 0;
}//上面的代码输出 可能输出下面的结果
// data.i: 42 
// data.f: 3.14 
// data.str: Hello After setting data.str, data.i: 1819043144  // 这些值可能是未定义的行为 
// After setting data.str, data.f: 1.15282e+09 // 因为内存已经被str覆盖

在这里插入图片描述

89. struct和class的区别?

在这里插入图片描述

90. 为什么TCP握手是3次,不能是2次和4次吗?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day1

&#x1f525;个人主页&#xff1a;草莓熊Lotso &#x1f3ac;作者简介&#xff1a;C研发方向学习者 &#x1f4d6;个人专栏&#xff1a; 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言&#xff1a;生活是默默的坚持&#xff0c;毅力是永久的…

【软考中级网络工程师】知识点之 TCP 协议深度剖析

目录一、TCP 协议简介二、TCP 协议的特点2.1 面向连接2.2 可靠性高2.3 拥塞控制2.4 全双工通信2.5 高效性2.6 支持多种应用协议2.7 可靠的错误恢复三、TCP 协议的工作机制3.1 三次握手建立连接3.2 数据传输3.3 四次挥手关闭连接四、TCP 协议的数据包格式五、TCP 协议在实际应用…

操作系统1.5:操作系统引导

目录 总览 什么是操作系统引导&#xff1f; 磁盘里边有哪些相关数据? 操作系统引导(开机过程&#xff09; 总览 什么是操作系统引导&#xff1f; 操作系统引导(boot)——开机的时候&#xff0c;怎么让操作系统运行起来? 磁盘里边有哪些相关数据? 一个刚买来的磁盘(硬…

[鹧鸪云]光伏AI设计平台解锁电站开发新范式

1.[鹧鸪云]平台概述[鹧鸪云]是由鹧鸪云&#xff08;徐州&#xff09;信息技术有限公司倾力打造的&#xff0c;可以媲美‌PVsyst的光伏AI设计平台。它为光伏项目不同阶段的开发提供了快速设计、卫星地图设计、无人机3D设计、Unity3D设计、专业绘图设计与场区设计多种设计方式&am…

docker compose和docker-compose命令的区别

Docker Compose 有两种命令形式&#xff1a;docker compose&#xff08;空格连接&#xff09;docker-compose&#xff08;短横线连接&#xff09;其核心区别如下&#xff1a;一、技术特性docker-compose&#xff08;短横线&#xff09;独立可执行文件&#xff1a;作为独立程序安…

基于Strands Agent开发辅助阅读Agent

序 本篇由来&#xff0c;在COC上我当面感谢了组委会和姜宁老师&#xff0c;随即被姜宁老师催稿&#xff0c;本来当天晚上写了一个流水账&#xff0c;感觉甚为不妥。于是决定慢慢写&#xff0c;缓缓道来。要同时兼顾Show me the code&#xff0c;Show me the vide。希望能形成一…

20250807简单树上问题

引入 树是一种特殊的图&#xff0c;因其看起来像一颗倒挂的树而得名。 树有许多等价的形式化定义&#xff0c;我们这里只取一个&#xff1a;nnn个点n−1n-1n−1条边的无向连通图。 树的直径 定义树上任意两点之间最长的简单路径为树的直径。 一棵树可能有很多直径&#xff0c…

诺基亚就4G/5G相关专利起诉吉利对中国汽车及蜂窝模组企业的影响

诺基亚于2025年7月18日向欧洲统一专利法院&#xff08;UPC&#xff09;曼海姆分庭和德国慕尼黑法院提起诉讼&#xff0c;控诉中国吉利控股集团及其极氪、领克、路特斯、Smart等关联品牌在未经许可的情况下使用诺基亚4项蜂窝通信标准必要专利 。涉案专利包括1项覆盖4G/5G的标准必…

Kotlin反射详解

反射是一种机制&#xff0c;它允许我们在运行时检查、修改和操作类或对象的内部结构。反射开启了动态编程的可能性&#xff0c;在开发库、框架或工具等场景中非常有用。Java 中的反射 在 Java 中&#xff0c;反射一直是实现动态编程的重要基石。它允许开发者在不提前知道类名的…

学习嵌入式-IMX6ULL学习——中断

volatile&#xff1a;易变的&#xff0c;防止系统优化对寄存器做处理的时候使用&#xff0c;在进行写1清零操作时&#xff0c;防止该操作被系统优化&#xff1b;一、GIC通用中断控制器1.GIC通用中断控制器GIC接收众多外部中断&#xff0c;然后对其进行处理&#xff0c;最终通过…

HENGSHI SENSE 6.0 功能-AI 查数助手

面向所有AI Agent开放BI和数据分析能力 AI 查数助手 6.0版本中&#xff0c;我们AI助手的优化是比较深入且全面的。从问答效率到集成能力&#xff0c;都得到了大的跃升&#xff0c;是智能问数应用场景的重大升级以及体验的全方位优化。我们优化了 AI 助手执行流程&#xff0c;…

降压型DCDC电源芯片推荐-芯伯乐XBL4001 40V/5A

在电子设备不断追求高性能与低功耗的今天&#xff0c;电源管理芯片的重要性不言而喻。芯伯乐主推的XBLW-XBL4001芯片&#xff0c;凭借其出色的设计与稳定的性能&#xff0c;为电源管理领域带来了一款实用的新选择。一、芯片概述XBLW-XBL4001是一款降压型&#xff08;Buck&#…

uni-app app端安卓和ios如何申请麦克风权限,唤起提醒弹框

代码包含功能如下&#xff1a; 1、判断推送权限是否开启 2、判断定位权限是否开启 3、判断麦克风权限是否开启 4、判断相机权限是否开启 5、判断相册权限是否开启 6、判断通讯录权限是否开启 7、判断日历权限是否开启 8、判断备忘录权限是否开启 9、Android权限查询 10、检查系…

关于 Rust 异步(无栈协程)的相关疑问

这是一个记录问题求助的文章。关于 waker 与运行时的合作方式我肤浅地学习了 Rust 异步底层实现原理&#xff0c;关于 Future、waker 和运行时等。关于 waker 我有三点猜测&#xff1a;waker 是由实现执行器的人提供的在执行器中会调用 epoll_wait&#xff0c;epoll 返回 fd&am…

stm32项目(25)——基于stm32的植物生长箱环境监测系统

1.实现功能 测 环境温湿度、光照强度、土壤湿度、水箱水位 手机APP显示 温度过低-->打开加热板 湿度过低-->打开水泵 土壤湿度低-->开水泵 --->只要有指标低于阈值时 就蜂鸣器报警 光强弱-->补光 水位低-->抽水 OLED屏幕实时显示各种信息 分…

golang 基础案例_02

1.锁有时候我们的代码中可能会存在多个 goroutine 同时操作一个资源&#xff08;临界区&#xff09;的情况&#xff0c;这种情况下就会发生竞态问题&#xff08;数据竞态&#xff09;。(1)、互斥锁&#xff1b;(2)、读写互斥锁&#xff1b;(3)、sync.WaitGroup&#xff1b;(4)、…

C++算法·前缀和

前缀和(Prefix(Prefix(Prefix Sum)Sum)Sum)的定义 前缀和是一种高效处理区间求和问题的算法技巧 其核心思想是通过预处理构建一个前缀和数组 使得后续的区间和查询可以在常数时间O(1)O(1)O(1)内完成 核心概念 定义 给定一个数组a[1...n]a[1...n]a[1...n],其前缀和数组s[1...…

JavaEE 初阶第十七期:文件 IO 的 “管道艺术”(下)

专栏&#xff1a;JavaEE初阶起飞计划 个人主页&#xff1a;手握风云 目录 一、Java文件内容写入 1.1. OutputStream 二、字符流读取和写入 2.1. Reader 2.2. Writer 三、示例练习 3.1. 查找文件功能 一、Java文件内容写入 1.1. OutputStream OutputStream同样只是⼀个抽…

【liunx】web高可用---nginx

NGINX简介Nginx&#xff08;发音为 “engine x”&#xff09;是一款由俄罗斯程序员 Igor Sysoev 开发的 轻量级、高性能的 HTTP 和反向代理服务器&#xff0c;同时也是一个 IMAP/POP3/SMTP 代理服务器。自 2004 年首次发布以来&#xff0c;Nginx 凭借其 高并发处理能力、低内存…

FPGA+护理:跨学科发展的探索(二)

FPGA护理&#xff1a;跨学科发展的探索&#xff08;二&#xff09; 系列文章目录 FPGA护理&#xff1a;跨学科发展的探索&#xff08;一&#xff09; 文章目录FPGA护理&#xff1a;跨学科发展的探索&#xff08;二&#xff09;系列文章目录引言三、FPGA 在精神医学护理中的应用…