线程

常见问题

同步权限

在多线程 / 多进程并发时,为避免共享资源(如内存变量、硬件设备、文件)被同时修改导致的数据不一致,需要通过 “同步机制” 控制谁能访问资源 ——“获取同步权限” 就是线程 / 进程申请这种访问资格的过程。

v4 = _InterlockedCompareExchange(a1, 1, 0);
  • 第一个参数 a1:指向目标内存地址的指针(通常是一个共享变量,如 LONG* 类型),即要操作的 “共享资源标记”。
  • 第二个参数 1:当比较成功时,要写入目标内存地址的 “新值”。
  • 第三个参数 0:“预期值”,即我们认为目标内存当前应该有的值。
  • 返回值是操作前目标内存地址(a1 指向的地址)中的原始值
// 定义一个共享的“锁标记”,0表示未占用,1表示已占用
LONG lock_flag = 0;// 线程A尝试获取锁
LONG original = _InterlockedCompareExchange(&lock_flag, 1, 0);
if (original == 0) {//获取到权限// 执行临界区操作...// 操作完成后释放锁(如将lock_flag设回0)
} else {// 获取锁失败(锁已被其他线程占用)// 可选择等待、重试或放弃
}

临界区

临界区(Critical Section) 指的是一段 “不能被多个线程同时执行” 的代码片段,当一个线程正在执行临界区代码时,其他线程必须等待该线程执行完毕,才能进入同一临界区。

原子性

“原子”(Atomic)描述的是一个不可分割、不可中断的操作单元。一个 “原子操作” 要么完整地执行完毕,要么完全不执行,中间不会被任何其他线程、进程或中断打断,不存在 “执行到一半” 的中间状态。

假设两个线程(Thread A、Thread B)同时对共享变量 count(初始值为 0)执行 count += 1 操作。
在底层会拆分为 3 个 CPU 指令:

  1. 从内存读取 count 的值到 CPU 寄存器(如 mov eax, [count]);
  2. 寄存器中的值加 1(如 inc eax);
  3. 将寄存器的值写回内存(如 mov [count], eax)。

如果操作不原子,可能出现以下 “交错执行”:

  • Thread A 执行步骤 1:读取 count=0 到寄存器;
  • 此时 CPU 切换到 Thread B,Thread B 执行步骤 1-3:读取 count=0 → 加 1→ 写回 count=1
  • CPU 切回 Thread A,继续执行步骤 2-3:寄存器值加 1(0→1)→ 写回 count=1

最终 count 的结果是 1,但预期是 2。

原子操作的本质

“原子性” 需要硬件(CPU)提供底层支持,再配合软件(操作系统、编程语言库)封装成易用的接口。

硬件层:CPU 的原子指令支持
不同架构的 CPU 会提供专门的 “原子操作指令”,确保单个指令的不可分割性:

  • x86/x86_64 架构:通过 lock 前缀实现原子性(如 lock cmpxchglock inc)。lock 前缀会让 CPU 在执行指令期间 “锁定系统总线”,阻止其他 CPU 核心同时访问该内存地址,确保指令执行不被打断;
  • ARM 架构:提供 ldrex(原子加载)、strex(原子存储)等指令,通过 “独占访问内存” 机制实现原子性;
  • RISC-V 架构:通过 amoswap.wamoadd.w 等 “原子内存操作指令”(AMO 指令)实现原子性。

这些硬件指令是 “原子操作” 的基石 —— 软件层面的原子接口(如 C++ 的 std::atomic、Windows 的 _InterlockedXXX)本质都是对这些 CPU 指令的封装。

软件层:原子操作的封装与扩展
硬件指令通常只支持 “单个内存地址的简单操作”(如加 1、比较交换),软件会在此基础上封装更灵活的原子操作:

原子操作

原子的交换操作
_InterlockedExchange(state_lock, 2);
  • 第一个参数 state_lock:指向目标内存地址的指针(通常是一个共享变量,如 LONG* 类型),即要被修改的 “状态标记”。
  • 第二个参数 2:要写入目标内存地址的 “新值”。
  • 返回值:原子地将 state_lock 指向的内存值更新为 2,同时返回该内存地址在更新前的原始值
原子比较交换操作
_InterlockedCompareExchange(&lock_flag, 1, 0);

参数1:类型为 volatile LONG*(指向 32 位有符号整数的指针),要操作的目标内存地址
参数2:类型为 LONG(32 位有符号整数),比较成功后要写入目标地址的值
参数3:类型为 LONG,表示预期的目标地址当前值(即 “旧值”)。
返回值:返回值为 LONG 类型,即目标地址在操作执行前的原始值

// 定义一个共享的“锁标记”,0表示未占用,1表示已占用
LONG lock_flag = 0;// 线程A尝试获取锁
LONG original = _InterlockedCompareExchange(&lock_flag, 1, 0);
if (original == 0) {//获取到权限// 执行临界区操作...// 操作完成后释放锁(如将lock_flag设回0)
} else {// 获取锁失败(锁已被其他线程占用)// 可选择等待、重试或放弃
}
原子地将目标变量的值加
_InterlockedIncrement(dword_14002BFF0);

参数dword_14002BFF0 是一个 LONG 类型(32 位)的共享变量(通常是全局或多线程可见的变量),表示要进行递增操作的目标。

返回值:函数返回递增后的新值LONG 类型)。

原子减 1
_InterlockedDecrement(a1)

参数:类型为 volatile LONG*(指向 32 位有符号整数的指针)
返回值:类型为 LONG(32 位有符号整数),表示减 1 操作完成后的结果值(即 *a1 - 1 的结果)。

线程同步

SRW 锁

SRW 锁支持两种获取模式:

  • 共享模式(Shared Mode):多个线程可同时获取,适用于 “只读操作” 场景(多个读者可并行访问资源)。
  • 独占模式(Exclusive Mode):仅允许一个线程获取,适用于 “修改操作” 场景(写者需独占资源)。
初始化SRW
void InitializeSRWLock(PSRWLOCK SRWLock
);

参数:PSRWLOCKSRWLOCK* 的类型别名,指向 SRWLOCK 结构体(轻量级读写锁的核心数据结构)。

核心作用:初始化 SRW 锁对象的内部状态,初始化后的 SRW 锁可通过以下函数实现读写分离的同步

  • 读操作:AcquireSRWLockShared(获取共享锁)和 ReleaseSRWLockShared(释放共享锁)。
  • 写操作:AcquireSRWLockExclusive(获取独占锁)和 ReleaseSRWLockExclusive(释放独占锁)。
读操作
void AcquireSRWLockShared(PSRWLOCK SRWLock
);

参数 SRWLock 是指向 SRWLOCK 结构体的指针,让线程以 “共享模式”(只读模式)安全地获取轻量级读写锁(SRW Lock)

void ReleaseSRWLockShared(PSRWLOCK SRWLock
);

参数 SRWLock 是指向 SRWLOCK 结构体的指针(PSRWLOCKSRWLOCK*),表示要释放的轻量级读写锁对象。

写操作
void AcquireSRWLockExclusive(PSRWLOCK SRWLock
);

让线程以 “独占模式” 获取 SRW 锁,确保对共享资源的修改操作(写操作)具有原子性

临界区

进出临界区
EnterCriticalSection(&stru_14002C030)

参数:类型LPCRITICAL_SECTION(即 CRITICAL_SECTION*,指向临界区结构体的指针)。
无返回值
作用:让当前线程 “获取临界区的访问权”

LeaveCriticalSection(&stru_14002C030);

作用:释放临界区

初始化临界区
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection
);

参数 lpCriticalSection 是指向 CRITICAL_SECTION 结构体的指针(LPCRITICAL_SECTIONCRITICAL_SECTION* 的类型别名),表示要初始化的临界区对象。

异常:如果初始化失败(通常是由于系统资源不足),函数会触发一个异常(而非返回错误码)。因此在实际使用中,可能需要配合异常处理(如 __try/__except)捕获潜在错误。

作用:初始化临界区对象的内部状态,初始化后的临界区可通过 EnterCriticalSection(进入临界区)和 LeaveCriticalSection(离开临界区)实现。

使用示例

// 定义临界区对象(全局或栈上)
CRITICAL_SECTION CriticalSection;// 初始化临界区(通常在程序启动或模块初始化时调用)
InitializeCriticalSection(&CriticalSection);// 多线程场景中使用
void ThreadFunc() {// 进入临界区(获取同步权限)EnterCriticalSection(&CriticalSection);// 执行需要同步的操作(如访问共享资源)AccessSharedResource();// 离开临界区(释放同步权限)LeaveCriticalSection(&CriticalSection);
}// 程序退出前销毁临界区(释放资源)
DeleteCriticalSection(&CriticalSection);
临界区同步
TryEnterCriticalSection()

参数:LPCRITICAL_SECTION lpCriticalSection指向 CRITICAL_SECTION 结构体的指针(与 EnterCriticalSection 相同),表示要尝试进入的临界区对象,需提前通过 InitializeCriticalSection 初始化。

返回值: TRUE(非 0 值):表示成功进入临界区,FALSE(0):表示未能进入临界区

作用:非阻塞尝试进入临界区,与 EnterCriticalSection 的 “阻塞等待” 不同,TryEnterCriticalSection 的核心特点是 “尝试进入,失败立即返回”,适用于以下场景:

  • 当线程只需 “短暂尝试” 获取临界区,若失败则执行其他任务(而非等待),避免线程阻塞。
  • 实现 “超时等待” 逻辑(结合循环和 Sleep,多次尝试后放弃)。
临界区状态结构体
00000000 struct _RTL_CRITICAL_SECTION // sizeof=0x28
00000000 {                                       // XREF: .data:CriticalSection/r
00000000                                         // .data:__rtl_critical_section/r
00000000     PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
00000008     LONG LockCount;
0000000C     LONG RecursionCount;
00000010     HANDLE OwningThread;
00000018     HANDLE LockSemaphore;
00000020     ULONG_PTR SpinCount;
00000028 };

线程调度

CPU 时间片

SwitchToThread() 

返回值:

  • 返回 TRUE(非 0 值):表示当前有其他 “就绪状态” 的线程(属于同一优先级或更高优先级)被调度执行;
  • 返回 FALSE(0):表示当前没有其他就绪线程可调度(即系统中只有当前线程可运行)。

作用:

  • 当前线程主动放弃剩余的 CPU 时间片,让操作系统调度器重新选择一个就绪线程(通常是同优先级的其他线程)运行。

自旋等待

while ( 1 )
{v6 = _InterlockedCompareExchange(a1, 1, 0); // 原子比较交换:尝试将a1从0改为1if ( !v6 ) // v6=0表示成功获取权限(a1原本为0,已改为1)break;if ( v6 != 2 ) // 若a1当前为1(被其他线程占用),则主动让出CPUSwitchToThread();if ( *a1 == 2 ) // 若等待期间a1变为2(终止),则返回0return 0LL;
}

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

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

相关文章

一台设备管理多个 GitHub 账号:从配置到切换的完整指南

一台设备管理多个 GitHub 账号:从配置到切换的完整指南 在日常开发中,我们经常需要在同一台电脑上使用多个 GitHub 账号(比如个人账号和工作账号)。但默认情况下,Git 会优先使用全局配置的账号,导致推送代…

即插即用,秒入虚拟:TouchDIVER Pro 触觉手套 赋能 AR/VR 高效交互

一、即插即用,零门槛开启沉浸之旅 在XR(扩展现实)技术高速发展的今天,用户对“真实感”的追求愈发迫切。Weart公司旗下旗舰产品TouchDIVER Pro触觉手套,凭借无需适配器、无需复杂设置的极简设计,打破传统触…

GitHub热榜项目 - 日榜之应用场景与未来发展趋势

一、引言GitHub热榜项目 - 日榜呈现出丰富多样的技术成果,这些项目蕴含着巨大的应用潜力,并且对未来数智化技术的发展有着重要的指示作用。深入探究其应用场景以及未来发展趋势,能让我们更好地把握技术发展方向,将这些前沿技术应用…

Linux网络:socket编程TCP

文章目录前言一,服务器端流程1-1 绑定协议1-2 绑定IP和端口1-3 监听客户端1-4 接收连接1-5 收发数据1-6 关闭连接1-7 服务端整体代码二,客户端流程2-1 指定地址和端口2-2 连接服务器2-3 发送消息2-4 客户端整体代码前言 TCP 的通信过程就像两个人打电话…

飞书智能查询机器人搭建说明文档

飞书智能查询机器人搭建说明文档 一、使用手册 1. 创建飞书机器人应用 如果仅需对接已有机器人应用则可跳过该步骤(建议各业务部门独立使用各自的机器人应用)。在飞书开发者后台中创建企业自建应用,添加机器人应用能力并申请对应的身份权限…

蓝色系列包装行业网站 适合企业站,带手机版自适应

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 蓝色通用企业网站是基于SDCMS四合一企业网站管理系统开发的模板,适合企业站,带手机版。 四网合一企业网站管理系统是一个以PHPMySQL/Sqlite进行开发的四网合一网…

【大模型:知识图谱】--6.Neo4j DeskTop安装+使用

上一期讲了图知识库的安装, 【图数据库】--Neo4j 安装_neo4j安装-CSDN博客 现在来看看可视化管理程序:Neo4j DeskTop的安装. 需要先安装java环境,具体看上面 目录 1.Neo4j DeskTop版下载 2.Neo4j DeskTop版安装 3.Neo4j DeskTop版使用 …

Python爬虫实战——使用NetNut网页解锁器获取亚马逊电商数据

文章目录一、电商数据的作用1.1 支撑科学决策,降低试错成本1.2 提升运营效率,实现降本增效1.3 深化用户理解,驱动个性化服务1.4 监测竞品动态,制定差异化策略1.5 驱动产品创新,满足用户需求二、爬取目标三、环境准备四…

超越NAT:如何构建高效、安全的内网穿透隧道

在敏捷开发和分布式协作成为主流的今天,开发者需要一个能够将本地开发环境瞬间暴露给公网的能力,以便进行演示、联调或处理回调。传统方案如配置路由器端口映射或部署VPN,不仅繁琐且存在安全风险。内网穿透技术,特别是以 ngrok、Z…

第二十三章 ESP32S3 RTC 实验

本章介绍 ESP32-S3 实时时钟(RTC)的使用,实时时钟能为系统提供一个准确的时间,即时系统复位或主电源断电, RTC 依然能够运行,因此 RTC 也经常用于各种低功耗场景。通过本章的学习,将学习到 RTC …

Java 轻松实现 Markdown 转 Word、PDF、HTML

在软件开发和技术写作领域,Markdown 已成为一种被广泛使用的轻量级标记语言。它的语法简洁,书写效率高,非常适合快速记录笔记、撰写技术文档或博客文章。但在实际应用中,Markdown 文件往往需要被转换为更通用的格式,例…

Kafka系列之:Kafka broker does not support the ‘MetadataRequest_v0‘ Kafka protocol.

Kafka系列之:Kafka broker does not support the MetadataRequest_v0 Kafka protocol. 一、完整报错 二、错误原因 三、解决方法 一、完整报错 kafka.errors.IncompatibleBrokerVersion: IncompatibleBrokerVersion: Kafka broker does not support the ‘MetadataRequest_v0’…

开源AI红队工具“Red AI Range“助力发现、分析与缓解AI系统漏洞

开源AI红队平台Red AI Range(RAR)正在改变安全专业人员评估和强化AI系统的方式。该平台通过模拟真实攻击场景,利用容器化架构和自动化工具,简化了AI特有漏洞的发现、分析和缓解流程。**核心功能** 1. 武器库/目标按钮可快速启动…

SQL 数据库简介

SQL(Structured Query Language)是一种用于管理和操作关系型数据库的标准语言。关系型数据库以表格形式存储数据,并通过行和列的结构化方式组织信息。SQL 提供了一套强大的命令,用于查询、插入、更新和删除数据,以及管…

SpringBoot4与Spring7发布:云原生深度进化

Spring Boot 4和Spring Framework 7带来基础要求升级、模块化改进、API版本化、声明式HTTP客户端、弹性注解等重大特性,标志着Java开发生态向云原生时代的深度进化。 近日,Spring生态迎来了自2022年以来最具里程碑意义的更新——Spring Boot 4和Spring …

基于Spring Boot与Micrometer的系统参数监控指南

如何为你的Spring Boot应用装上一个功能强大的监控仪表盘在现代微服务架构中,系统监控已成为保障应用稳定性的关键环节。通过有效的监控,我们可以实时了解应用的运行状态,及时发现并解决性能问题。本文将介绍如何使用Micrometer及其注册表&am…

【运维】-- 前端会话回放与产品分析平台之 openreplay

目录 OpenReplay 项目分析 1、项目概览 2、关键特性 3、代码结构(Monorepo) 4、技术栈与语言占比 5、部署与交付 6、社区与支持 7、版本与活跃度(截至仓库页面所示) 8、适用场景 9、优势与注意事项 10、落地建议&#…

NineData社区版 V4.5.0 正式发布!运维中心新增细粒度任务权限管理,新增MySQL至Greenplum全链路复制对比

NineData 社区版 V4.5.0 正式发布!在数据复制方面,新增 MySQL 至 Greenplum 全链路复制对比,并优化全局 DDL 管控、MySQL/PostgreSQL/MongoDB 同构性能。在数据库 DevOps 方面,新增支持 AWS RDS 全系列及阿里云 PolarDB&#xff0…

discuz所有下载版本和升级工具

下载版本: Discuz! 每日构建版下载 - DiscuzX 3.x Daily Build Download Site SC是简体中文 TC是繁体中文 可能你需要其他版本: Discuz!官方网站 - 开放、连接、共赢 下载简体中文就好。 升级工具: 升级程序下载地址 https://gitee.com/oldhuhu/DiscuzX34235.git(…

【开题答辩全过程】以 “红色枣庄”旅游网站为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人,语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…