C# SpinLock 类 使用详解
总目录
前言
SpinLock 是 C# 中一种轻量级的自旋锁,属于 System.Threading 命名空间,专为极短时间锁竞争的高性能场景设计。它通过忙等待(自旋)而非阻塞线程来减少上下文切换开销,适用于锁持有时间极短(如微秒级)的多线程操作。
一、SpinLock 概述
SpinLock 是 .NET Framework 4.0+ 引入的轻量级同步锁机制,位于 System.Threading 命名空间下。与 Monitor 或 lock 不同,SpinLock 通过“自旋”等待资源释放(忙等待),而非立即让线程进入阻塞状态。这减少了上下文切换的开销,但可能增加 CPU 占用。
1. 核心概念
- 自旋机制:通过循环检查锁的状态来避免线程进入阻塞状态,适合于短时间等待。
- 轻量级:
- 相比 Monitor(即 lock 关键字),SpinLock 更高效,但需手动管理锁的生命周期。
- 适用于高频率、短时间的锁定操作,如等待某个资源的状态变化。
- 线程追踪:可启用线程 ID 追踪(通过构造函数参数),辅助调试死锁问题。
- 非递归锁:默认不支持递归获取锁(同一线程重复获取会抛出异常)。
- 自适应行为:SpinLock 会根据系统负载自动调整其行为,最初进行忙等待,随后如果等待时间较长,则会切换到更高效的阻塞等待。
2. 适用场景
- 极短时间的锁持有(如 <1ms 的临界区操作)
- 例如,在等待某个标志位的变化时,使用忙等待可以减少上下文切换的开销。
- 高并发、低竞争环境(如多核 CPU 频繁访问共享资源)
- 避免阻塞等待:在某些实时性要求较高的应用中,忙等待可以避免因阻塞等待导致的延迟。
- 替代
lock或Monitor以优化性能(需实际测试验证)- SpinLock 的使用和 Monitor 比较相似,都是处理线程安全的一种锁,只不过SpinLock 是自旋锁
二、主要方法和属性
1. 初始化
SpinLock spinLock = new SpinLock(); // 默认启用线程跟踪(调试用)
SpinLock spinLockNoTracking = new SpinLock(enableThreadOwnerTracking: false); // 禁用跟踪(提升性能)
- enableThreadOwnerTracking:若为 true,记录持有锁的线程 ID,方便调试,但略微增加开销。
2. 主要方法和属性
| 方法/属性 | 作用 |
|---|---|
Enter(ref bool lockTaken) | 尝试获取锁,并将 lockTaken 设置为 true 表示成功获取锁, 设置为 false 表示未能获取锁。 |
Exit() | 释放锁。 |
IsHeld | 获取一个值,指示当前线程是否持有该锁。 |
IsHeldByCurrentThread | 获取一个值,指示当前线程是否持有该锁。 |
SpinCount | 获取或设置旋转计数,表示忙等待的最大次数。 |
3. 使用示例
1)获取与释放锁
bool lockTaken = false;
try
{spinLock.Enter(ref lockTaken); // 尝试获取锁// 临界区操作(如修改共享资源)
}
finally
{if (lockTaken) spinLock.Exit(); // 必须释放锁
}
lockTaken参数:必须通过ref传递,用于检测是否成功获取锁。- 必须使用
try-finally:确保锁在异常时也能释放,避免死锁。
2)获取与释放锁高级方法:TryEnter
bool lockTaken = false;
spinLock.TryEnter(TimeSpan.FromMilliseconds(50), ref lockTaken); // 设置超时
if (lockTaken)
{try { /* 临界区 */ }finally { spinLock.Exit(); }
}
else
{// 超时处理(如记录日志或重试)
}
- 超时机制:避免无限自旋,适用于潜在的高竞争场景。
三、性能优化示例
1. 线程安全计数器
using System;
using System.Threading;
using System.Threading.Tasks;class Program
{static SpinLock spinLock = new SpinLock();static int sharedCounter = 0;static void Main(string[] args){// 启动多个任务以并发地修改共享资源var tasks = new List<Task>();for (int i = 0; i < 5; i++){int j= i+1;tasks.Add(Task.Run(() => IncrementCounter($"Task {j}")));}// 等待所有任务完成Task.WaitAll(tasks.ToArray());Console.WriteLine($"最终计数值: {sharedCounter}");}static void IncrementCounter(string taskName){bool lockTaken = false;try{spinLock.Enter(ref lockTaken);Console.WriteLine($"{taskName} 进入临界区");sharedCounter++; // 模拟对共享资源的操作Thread.Sleep(100); // 模拟一些工作Console.WriteLine($"{taskName} 退出临界区");}finally{if (lockTaken){spinLock.Exit();}}}
}
using System.Threading;class Program
{static SpinLock spinLock = new SpinLock();static int _counter = 0;static void Main(){Parallel.For(0, 1000, _ => IncrementCounter());Console.WriteLine($"最终计数: {_counter}"); // 输出 1000}static void IncrementCounter(){bool lockTaken = false;try{spinLock.Enter(ref lockTaken);_counter++;}finally{if (lockTaken) spinLock.Exit();}}
}
2. 示例2
using System;
using System.Threading;class Program
{static SpinLock spinLock = new SpinLock();static int sharedValue = 0;static void Main(){// 启动多个任务Task[] tasks = new Task[10];for (int i = 0; i < 10; i++){tasks[i] = Task.Run(() => IncrementSharedValue());}// 等待所有任务完成Task.WaitAll(tasks);Console.WriteLine($"最终结果:{sharedValue}");}static void IncrementSharedValue(){bool lockTaken = false;SpinWait spinWait = new SpinWait();try{spinLock.TryEnter(ref lockTaken);while (!lockTaken){spinWait.SpinOnce(); // 自定义自旋策略}sharedValue++;}finally{if (lockTaken){spinLock.Exit(); // 释放锁}}}
}
四、注意事项
1. 不可递归获取
// 错误示例:同一线程重复获取 SpinLock 导致死锁!
SpinLock spinLock = new SpinLock();
bool lockTaken1 = false, lockTaken2 = false;
spinLock.Enter(ref lockTaken1);
spinLock.Enter(ref lockTaken2); // 此处会死锁!
- SpinLock 不支持递归:同一线程多次获取锁会引发死锁。
- 替代方案:使用
Monitor或Mutex支持递归的锁。
2. 避免值类型陷阱
class MyClass
{private readonly SpinLock spinLock; // 错误!readonly 结构体可能导致副本问题public void Method(){bool lockTaken = false;spinLock.Enter(ref lockTaken); // 操作的是 spinLock 的副本!}
}
- SpinLock 是结构体:避免作为
readonly字段,否则可能因值拷贝导致锁失效。
3. 避免长时间自旋
适用场景:锁持有时间极短(如 <1μs)。
长时间自旋的代价:浪费 CPU 资源,应改用 Monitor 或混合锁(如 SpinWait + Thread.Yield)。
4. 线程追踪模式
- 启用追踪:初始化时设置 enableThreadOwnerTracking=true,可检测锁持有线程。
- 调试辅助:通过 IsHeldByCurrentThread 检查当前线程是否持有锁。
if (spinLock.IsHeldByCurrentThread)
{// 当前线程已持有锁
}
五、何时选择 SpinLock?
| 场景 | 推荐锁类型 |
|---|---|
| 锁持有时间极短(纳秒级) | SpinLock |
| 锁持有时间较长 | Monitor/lock、Semaphore |
| 需要递归锁 | Monitor、Mutex |
| 跨进程同步 | Mutex、Semaphore |
六、最佳实践
- 严格限制锁范围:确保临界区代码尽可能简短。
- 禁用递归:避免因递归调用导致死锁。
- 异常处理:始终使用 try-finally 确保锁释放。
- 性能测试:通过基准测试验证是否适合场景(如 BenchmarkDotNet)。
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
相关文章:
C# SpinLock 类 使用详解
总目录 前言 SpinLock 是 C# 中一种轻量级的自旋锁,属于 System.Threading 命名空间,专为极短时间锁竞争的高性能场景设计。它通过忙等待(自旋)而非阻塞线程来减少上下文切换开销,适用于锁持有时间极短(如…...
【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题
【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题 【承接商业广告,如需商业合作请+v17740568442】 文章目录 【linux】在 Linux 上部署 DeepSeek-r1:32/70b:解决下载中断问题问题描述:解决方法方法一:手动中断并重启下载方法二:使用 Bash 脚本自动化下载在…...
机器学习所需要的数学知识【01】
总览 导数 行列式 偏导数 概理论 凸优化-梯度下降 kkt条件...
4.【线性代数】——矩阵的LU分解
四 矩阵的LU分解 1. AB的逆矩阵2. 转置矩阵3. ALU3.1 2x2矩阵3.2 3x3矩阵3.3 nxn的矩阵分解的次数? 1. AB的逆矩阵 { ( A B ) ( B − 1 A − 1 ) I ( B − 1 A − 1 ) ( A B ) I ⇒ ( A B ) − 1 B − 1 A − 1 \begin{cases} (AB)(B^{-1}A^{-1}) I\\ (B^{-1}A^…...
【清晰教程】本地部署DeepSeek-r1模型
【清晰教程】通过Docker为本地DeepSeek-r1部署WebUI界面-CSDN博客 目录 Ollama 安装Ollama DeepSeek-r1模型 安装DeepSeek-r1模型 Ollama Ollama 是一个开源工具,专注于简化大型语言模型(LLMs)的本地部署和管理。它允许用户在本地计算机…...
Spring Cloud工程搭建
目录 工程搭建 搭建父子工程 创建父工程 Spring Cloud版本 创建子项目-订单服务 声明项⽬依赖 和 项⽬构建插件 创建子项目-商品服务 声明项⽬依赖 和 项⽬构建插件 工程搭建 因为拆分成了微服务,所以要拆分出多个项目,但是IDEA只能一个窗口有一…...
使用Redis实现分布式锁,基于原本单体系统进行业务改造
一、单体系统下,使用锁机制实现秒杀功能,并限制一人一单功能 1.流程图: 2.代码实现: Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderSe…...
【MediaTek】 T750 openwrt-23.05编 cannot find dependency libexpat for libmesode
MediaTek T750 T750 采用先进的 7nm 制程,高度集成 5G 调制解调器和四核 Arm CPU,提供较强的功能和配置,设备制造商得以打造精巧的高性能 CPE 产品,如固定无线接入(FWA)路由器和移动热点。 MediaTek T750 平台是一款综合的芯片组,集成了 5G SoC MT6890、12nm 制程…...
CHARMM-GUI EnzyDocker: 一个基于网络的用于酶中多个反应状态的蛋白质 - 配体对接的计算平台
❝ "CHARMM-GUI EnzyDocker for Protein−Ligand Docking of Multiple Reactive States along a Reaction Coordinate in Enzymes"介绍了 CHARMM-GUI EnzyDocker,这是一个基于网络的计算平台,旨在简化和加速 EnzyDock 对接模拟的设置过程&…...
c# 2025/2/17 周一
16. 《表达式,语句详解4》 20 未完。。 表达式,语句详解_4_哔哩哔哩_bilibili...
vite【详解】常用配置 vite.config.js / vite.config.ts
官网 https://cn.vitejs.dev/guide/ vite 常用配置 Vite 配置文件通常是 vite.config.js (使用 CommonJS 语法)或者 vite.config.ts(使用 TypeScript 语法),默认内容为 import { defineConfig } from vite import vue…...
最新智能优化算法: 阿尔法进化(Alpha Evolution,AE)算法求解23个经典函数测试集,MATLAB代码
一、阿尔法进化算法 阿尔法进化(Alpha Evolution,AE)算法是2024年提出的一种新型进化算法,其核心在于通过自适应基向量和随机步长的设计来更新解,从而提高算法的性能。以下是AE算法的主要步骤和特点: 主…...
用于可靠工业通信的5G-TSN集成原型:基于帧复制与消除可靠性的研究
论文标题 中文标题:用于可靠工业通信的5G-TSN集成原型:基于帧复制与消除可靠性的研究 英文标题:5G-TSN Integrated Prototype for Reliable Industrial Communication Using Frame Replication and Elimination for Reliability 作者信息 …...
HaProxy源码安装(Rocky8)
haproxy具有高性能、高可用性、灵活的负载均衡策略和强大的将恐和日志功能,是法国开发者 威利塔罗(Willy Tarreau)在2000年使用C语言开发的一个开源软件,是一款具 备高并发(一万以上)、高性能的TCP和HTTP负载均衡器,支持基于cookie的持久性&a…...
shell脚本备份MySQL数据库和库下表
目录 注意: 一.脚本内容 二.执行效果 三.创建定时任务 注意: 以下为对MySQL5.7.42版本数据库备份shell脚本参考运行备份的机器请确认mysqldump版本>5.7,否则备份参数--set-gtid-purgedOFF无效,考虑到一般数据库节点和备份…...
23. AI-大语言模型
文章目录 前言一、LLM1. 简介2. 工作原理和结构3. 应用场景4. 最新研究进展5. 比较 二、Transformer架构1. 简介2. 基本原理和结构3. 应用场景4. 最新进展 三、开源1. 开源概念2. 开源模式3. 模型权重 四、再谈DeepSeek 前言 AI 一、LLM LLM(Large Language Mod…...
Linux /dev/null
/dev/null 是 Linux 和类 Unix 系统中一个特殊且非常有用的设备文件,也被称为空设备。下面为你详细介绍它的特点、用途和使用示例。 特点 写入丢弃:当向 /dev/null 写入数据时,这些数据会被立即丢弃,不会被保存到任何地方&#…...
Unity CommandBuffer绘制粒子系统网格显示
CommandBuffer是 Unity 提供的一种在渲染流程中插入自定义渲染命令的机制。在渲染粒子系统时,常规的渲染流程可能无法满足特定的渲染需求,而CommandBuffer允许开发者灵活地设置渲染参数、控制渲染顺序以及执行自定义的绘制操作。通过它,可以精…...
Java延时定时刷新Redis缓存
延时定时刷新Redis缓存 一、背景 项目需求:订阅接收一批实时数据,每分钟最高可接收120万条数据,并且分别更新到redis和数据库中;而用户请求查询消息只是低频操作。资源限制:由于项目预算有限,只有4台4C16…...
智能硬件定位技术发展趋势
在科技飞速进步的当下,智能硬件定位技术作为众多领域的关键支撑,正沿着多元且极具创新性的路径蓬勃发展,持续重塑我们的生活与工作方式。 一、精度提升的极致追求 当前,智能硬件定位精度虽已满足诸多日常应用,但未来…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
STL 2迭代器
文章目录 1.迭代器2.输入迭代器3.输出迭代器1.插入迭代器 4.前向迭代器5.双向迭代器6.随机访问迭代器7.不同容器返回的迭代器类型1.输入 / 输出迭代器2.前向迭代器3.双向迭代器4.随机访问迭代器5.特殊迭代器适配器6.为什么 unordered_set 只提供前向迭代器? 1.迭代器…...
