当前位置: 首页 > web >正文

详解 .net9 内置 Lock 对象,更加现代化和灵活可控的锁对象

.NET 9 引入了全新的 System.Threading.Lock 类型,作为更现代、类型安全且具备递归支持的同步原语。与传统的基于 Monitor.Enter/lock(obj) 的方式不同,Lock 是一个具体的类,提供了更灵活的 API 和结构化编程模型。

  • Lock 类

Lock 是一个具体密封类,详细代码如下:

#region 程序集 System.Runtime, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.5\ref\net9.0\System.Runtime.dll
#endregionnamespace System.Threading
{//// 摘要://     Provides a mechanism for achieving mutual exclusion in regions of code between//     different threads.public sealed class Lock{//// 摘要://     Initializes a new instance of the System.Threading.Lock class.public Lock();//// 摘要://     Gets a value that indicates whether the lock is held by the current thread.//// 返回结果://     true if the current thread holds the lock; otherwise, false.public bool IsHeldByCurrentThread { get; }//// 摘要://     Enters the lock, waiting if necessary until the lock can be entered.//// 异常://   T:System.Threading.LockRecursionException://     The lock has reached the limit of repeated entries by the current thread. The//     limit is implementation-defined and is intended to be high enough that it would//     not be reached in normal situations.public void Enter();//// 摘要://     Enters the lock, waiting if necessary until the lock can be entered.//// 返回结果://     A System.Threading.Lock.Scope that can be disposed to exit the lock.//// 异常://   T:System.Threading.LockRecursionException://     The lock has reached the limit of repeated entries by the current thread. The//     limit is implementation-defined and is intended to be high enough that it would//     not be reached in normal situations.public Scope EnterScope();//// 摘要://     Exits the lock.//// 异常://   T:System.Threading.SynchronizationLockException://     The current thread does not hold the lock.public void Exit();//// 摘要://     Tries to enter the lock without waiting.//// 返回结果://     true if the lock was entered by the current thread; otherwise, false.//// 异常://   T:System.Threading.LockRecursionException://     The lock has reached the limit of repeated entries by the current thread. The//     limit is implementation-defined and is intended to be high enough that it would//     not be reached in normal situations.public bool TryEnter();//// 摘要://     Tries to enter the lock, waiting if necessary for the specified number of milliseconds//     until the lock can be entered.//// 参数://   millisecondsTimeout://     The number of milliseconds to wait until the lock can be entered. Specify Timeout.Infinite//     (////     -1////     ) to wait indefinitely, or////     0////     to not wait.//// 返回结果://     true if the lock was entered by the current thread; otherwise, false.//// 异常://   T:System.ArgumentOutOfRangeException://     millisecondsTimeout is less than////     -1////     .////   T:System.Threading.LockRecursionException://     The lock has reached the limit of repeated entries by the current thread. The//     limit is implementation-defined and is intended to be high enough that it would//     not be reached in normal situations.public bool TryEnter(int millisecondsTimeout);//// 摘要://     Tries to enter the lock, waiting if necessary until the lock can be entered or//     until the specified timeout expires.//// 参数://   timeout://     A System.TimeSpan that represents the number of milliseconds to wait until the//     lock can be entered. Specify a value that represents Timeout.Infinite (////     -1////     ) milliseconds to wait indefinitely, or a value that represents////     0////     milliseconds to not wait.//// 返回结果://     true if the lock was entered by the current thread; otherwise, false.//// 异常://   T:System.ArgumentOutOfRangeException://     timeout, after its conversion to an integer millisecond value, represents a value//     that is less than////     -1////     milliseconds or greater than Int32.MaxValue milliseconds.////   T:System.Threading.LockRecursionException://     The lock has reached the limit of repeated entries by the current thread. The//     limit is implementation-defined and is intended to be high enough that it would//     not be reached in normal situations.public bool TryEnter(TimeSpan timeout);//// 摘要://     Represents a System.Threading.Lock that might have been entered.public ref struct Scope{//// 摘要://     Exits the lock if the System.Threading.Lock.Scope represents a lock that was//     entered.//// 异常://   T:System.Threading.SynchronizationLockException://     The System.Threading.Lock.Scope represents a lock that was entered and the current//     thread does not hold the lock.public void Dispose();}}
}

🔒 Lock 对象概述

System.Threading.Lock 是一种 轻量级互斥锁(Lightweight Mutex,用于控制多线程对共享资源的访问,确保同一时间只有一个线程可以执行特定代码段

它适用于需要 显式管理加锁和解锁操作 的场景,比如在 非阻塞编程 中尝试获取锁或使用 超时机制

✅ 特性

  • 支持递归锁定(默认允许同一个线程多次进入)
  • 提供阻塞、非阻塞和带超时的获取方式
  • 使用 Scope 结构实现 RAII 风格的自动释放
  • 线程亲和性强,可判断当前线程是否持有锁

RAII (Resource Acquisition Is Initialization) 是一种 C++ 编程范式,其核心思想是 将资源的生命周期绑定到对象的生命周期上,即:

  • 资源的获取发生在对象构造时;
  • 资源的释放发生在对象析构时。

.NET 9System.Threading.Lock 中,Scope 是一个 ref struct,通过调用 EnterScope() 方法获得。它实现了类似 RAII 的模式:

//
// 摘要:
//     Represents a System.Threading.Lock that might have been entered.
public ref struct Scope
{//// 摘要://     Exits the lock if the System.Threading.Lock.Scope represents a lock that was//     entered.//// 异常://   T:System.Threading.SynchronizationLockException://     The System.Threading.Lock.Scope represents a lock that was entered and the current//     thread does not hold the lock.public void Dispose();
}// 使用 EnterScope()
using (var scope = myLock.EnterScope())
{// 执行临界区代码
} // 自动调用 scope.Dispose(),进而释放锁
解释 RAII 风格的自动释放

使用 Scope 结构实现 RAII 风格的自动释放 这句话的意思是:

  • 当你调用 EnterScope() 获取一个 Scope 实例时,锁已经被当前线程持有
  • 将该 Scope 实例放入 using 语句块中,当代码块结束时,会自动调用其 Dispose() 方法
  • Dispose() 方法内部,会调用 Exit() 来释放锁;
  • 这样就实现了 锁的获取和释放与代码块的进入和退出严格绑定,避免忘记释放锁或异常情况下锁未被释放的问题。

这种方式提升了代码的安全性和可读性,是现代 .NET 推荐使用的同步编程模型。

📌 构造函数

public Lock();

初始化一个新的 Lock 实例。默认情况下,该锁是可重入的(recursive),即同一个线程可以多次调用 Enter() 而不会死锁。


🧱 主要方法详解

1. void Enter()

阻塞当前线程直到成功获取锁。

var myLock = new Lock();myLock.Enter(); // 阻塞直到获取锁
try {// 访问共享资源
} finally {myLock.Exit();
}
抛出异常:
  • LockRecursionException: 如果递归次数超过限制(极少发生)

2. Scope EnterScope()

尝试进入锁,并返回一个 ref struct Scope,用于通过 using 自动释放锁。

using (myLock.EnterScope())
{// 安全访问共享资源
}
// 锁自动释放

这是推荐的方式,避免手动调用 Exit() 导致的资源泄漏。


3. void Exit()

释放锁。必须由当前持有锁的线程调用,否则抛出异常。

myLock.Enter();
try {// ...
} finally {myLock.Exit();
}
抛出异常:
  • SynchronizationLockException: 当前线程未持有锁

4. bool TryEnter()

尝试立即获取锁,不等待。

if (myLock.TryEnter())
{try {// 成功获取锁} finally {my7.Exit();}
}
else
{// 获取失败,跳过
}

返回值:

  • true: 成功获取锁
  • false: 锁已被其他线程占用

5. bool TryEnter(int millisecondsTimeout)

尝试在指定时间内获取锁。

bool lockTaken = myLock.TryEnter(1000); // 最多等待1秒
if (lockTaken)
{try { /* ... */ } finally { myLock.Exit(); }
}

参数:

  • millisecondsTimeout: 等待毫秒数,-1 表示无限等待,0 表示不等待

返回值:

  • true: 成功获取锁
  • false: 超时或未获取到

6. bool TryEnter(TimeSpan timeout)

同上,但接受 TimeSpan 参数。

bool lockTaken = myLock.TryEnter(TimeSpan.FromSeconds(2));

🧩 嵌套结构:Lock.Scope

这是一个 ref struct,用于封装锁的生命周期,推荐配合 using 使用。

using var scope = myLock.EnterScope();
// 执行临界区代码

scopedispose 时,会自动调用 Exit()

方法:
public void Dispose();

释放锁,若当前线程未持有锁则抛出异常。


🧠 内部原理简析

  • Lock 内部基于高效的自旋锁(SpinLock)+ 内核事件(Event)混合实现。
  • 初期尝试自旋几次以快速获取锁,失败后进入内核等待状态。
  • 支持递归锁定,默认递归深度限制较高(足够日常使用)。
  • 使用线程本地存储记录当前线程是否持有锁,保证 IsHeldByCurrentThread 的准确性。

🔍 属性:bool IsHeldByCurrentThread

检查当前线程是否持有该锁。

if (myLock.IsHeldByCurrentThread)
{Console.WriteLine("当前线程已持有锁");
}

适用于调试和日志记录,避免重复加锁导致死锁。


🧪 应用场景举例

场景 1:线程安全的计数器

private int _counter = 0;
private readonly Lock _lock = new();public void Increment()
{using (_lock.EnterScope()){_counter++;}
}public int GetCount()
{using (_lock.EnterScope()){return _counter;}
}

场景 2:线程安全的单例实现

  • 方式一:原生 Lock 实现
public class Singleton
{// 单例实例private static Singleton _instance;// 锁对象private static readonly Lock _lock = new();// 私有化无参构造函数private Singleton(){// 初始化逻辑}// 获取单例实例的方法public static Singleton Instance{get{// 先判断是否已创建,避免每次都加锁if (_instance == null){using (_lock.EnterScope()){// 再次检查是否为 null(双重检查锁定)if (_instance == null){_instance = new Singleton();}}}return _instance;}}// 其他方法实现
}
  • 方式二:使用 Lazy<T> & Lock 实现
public class Singleton
{private static readonly Lock _lock = new();private static readonly Lazy<Singleton> _lazyInstance = new(() =>{using (_lock.EnterScope()){return new Singleton();}});// 私有化无参构造函数private Singleton(){// 初始化逻辑}public static Singleton Instance => _lazyInstance.Value;// 其他方法实现
}

场景 3:带超时的缓存刷新

private readonly Lock _cacheLock = new();
private object _cachedData;public object? GetCachedData()
{if (_cacheLock.TryEnter(TimeSpan.FromSeconds(1))){try{if (_cachedData == null){_cachedData = FetchFromDatabase();}return _cachedData;}finally{_cacheLock.Exit();}}else{// 超时处理逻辑return null;}
}

场景 4:避免跨线程访问 UI 控件(WinForms)

private readonly Lock _uiLock = new();private void UpdateLabel(string text)
{using (_uiLock.EnterScope()){// 假设 label1 是 WinForm 上的控件if (label1.InvokeRequired){label1.Invoke(new Action(() => label1.Text = text));}else{label1.Text = text;}}
}

⚠️ 注意事项

  • 不要跨线程传递 Lock.Scope 实例。
  • 避免在锁内部进行长时间操作,影响并发性能。
  • 优先使用 EnterScope() + using 来确保锁释放。
  • 若需更高性能读写分离,请考虑 ReaderWriterLockSlim

🧾 总结

特性描述
类型互斥锁(Mutex)
是否递归是(默认)
是否公平否(先进先出无法保证)
是否托管资源
推荐使用方式EnterScope() + using

System.Threading.Lock.NET 9 中为现代化并发编程设计的新一代同步工具,相比传统的 lock(obj) 更加灵活可控,适合 高并发、异步、分布式 等复杂场景下的同步需求。

http://www.xdnf.cn/news/9253.html

相关文章:

  • 分贝计在评估噪音对老年人影响中的具体作用和应用方式
  • qml和JavaScript的QtObject是 QML 中用于存储无界面,纯数据。应用场景:计算器、遥控器、告警类型映射
  • SD08_解决由于anaconda版本过低无法安装高版本python的问题
  • 5分钟入门WPF和FluentValidation数据验证
  • opencv(C++) 图像滤波
  • Dataset和Dataloader
  • VR三维数字空间还原
  • 大模型(4)——Agent(基于大型语言模型的智能代理)
  • 计算机网络基础知识
  • 7000字基于 SpringBoot 的 Cosplay 文化展示与交流社区系统设计与实现
  • 批量文件重命名工具
  • Web安全测试-文件上传绕过-DVWA
  • 【机器学习基础】机器学习入门核心算法:K-近邻算法(K-Nearest Neighbors, KNN)
  • 高效多线程图像处理实战
  • pycharm 新UI 固定菜单栏 pycharm2025 中文版
  • 小样本分类新突破:QPT技术详解
  • Mac M1 安装 ffmpeg
  • winsock对话设计框架
  • 大咖课 | 后期-文本分析
  • 新编辑器编写指南--给自己的备忘
  • 【请关注】VC++ MFC常见异常问题及处理方法
  • 如何使用PHP创建一个安全的用户注册表单,包含输入验证、数据过滤和结果反馈教程。
  • 第三十三天打卡
  • Windows安装Docker部署dify,接入阿里云api-key进行rag测试
  • 新消息!阿里云ACP大模型认证有变化!
  • https下git拉取gitlab仓库源码
  • tmux 入门实用指南(面向远程 Linux 开发者)
  • 测试报告里都包含哪些内容?
  • 使用pnpm、vite搭建Phaserjs的开发环境
  • 常见的网络设备