在现代C#开发中,异步编程已成为处理高并发、高性能应用的标准方式。然而,当多个异步任务需要访问共享资源时,如何安全地进行同步就变得至关重要。本文将带你从零开始,深入理解C#异步锁(特别是支持递归获取的异步锁)的工作原理与实现方式,即使是编程新手也能轻松掌握。

传统的 lock 关键字在C#中用于同步线程,但它在异步方法中使用会导致死锁或阻塞线程池线程,因此不适用于 async/await 场景。
为了解决这个问题,.NET 提供了 SemaphoreSlim,我们可以基于它构建一个支持 await 的 异步锁(AsyncLock)。
在某些场景下,一个方法可能在已经持有锁的情况下再次调用自身(直接或间接递归),或者调用另一个也需要相同锁的方法。如果锁不支持递归获取,就会导致死锁。
例如:
async Task ProcessData(){ using (await _asyncLock.LockAsync()) { // 做一些操作 await SaveData(); // SaveData 也尝试获取同一个锁 }}async Task SaveData(){ using (await _asyncLock.LockAsync()) { // 保存数据 }}如果没有递归支持,SaveData() 将永远等待自己释放锁,造成死锁。
下面是一个完整的 RecursiveAsyncLock 实现,它支持同一个执行上下文(如同一个异步流)多次获取锁而不死锁:
using System;using System.Threading;using System.Threading.Tasks;public class RecursiveAsyncLock{ private readonly SemaphoreSlim _semaphore = new(1, 1); private readonly AsyncLocal<int> _recursionCount = new(); private readonly AsyncLocal<object> _owner = new(); public async Task<Releaser> LockAsync(CancellationToken cancellationToken = default) { var currentOwner = _owner.Value; if (currentOwner != null) { // 同一个异步上下文已持有锁,递增计数 _recursionCount.Value++; return new Releaser(this); } // 等待获取锁 await _semaphore.WaitAsync(cancellationToken); _owner.Value = new object(); _recursionCount.Value = 1; return new Releaser(this); } private void Release() { if (_owner.Value == null) throw new InvalidOperationException("Lock was not held."); var count = _recursionCount.Value; if (count > 1) { _recursionCount.Value = count - 1; } else { _recursionCount.Value = 0; _owner.Value = null; _semaphore.Release(); } } public struct Releaser : IDisposable { private readonly RecursiveAsyncLock _lock; public Releaser(RecursiveAsyncLock @lock) { _lock = @lock; } public void Dispose() { _lock?.Release(); } }}await 之后,也能识别是否是“同一个调用链”。IDisposable 实现 using 语法糖,确保锁被正确释放。class DataService{ private readonly RecursiveAsyncLock _lock = new(); public async Task Process() { using (await _lock.LockAsync()) { Console.WriteLine("Processing..."); await Save(); // 递归获取锁 } } private async Task Save() { using (await _lock.LockAsync()) { Console.WriteLine("Saving..."); await Task.Delay(100); } }}运行上述代码不会死锁,因为 RecursiveAsyncLock 允许同一个异步流多次进入。
RecursiveAsyncLock 的实例用于跨线程递归(它基于 AsyncLocal,仅限单个异步流)。AsyncLock(性能略优)。通过本文,你已经掌握了如何在C#中实现和使用支持递归获取的异步锁。这种模式在复杂的异步业务逻辑中非常有用,能有效避免死锁,同时保证线程安全。记住,合理使用 AsyncLock、理解 递归锁 的适用场景,是编写健壮异步代码的关键。
关键词回顾:C#异步锁、递归锁、AsyncLock、异步编程
本文由主机测评网于2025-12-21发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://vpshk.cn/20251211168.html