异步互斥锁 MutexLock 用锁和不用锁的对比
MutextLock\Program.cs
using System;
using System.Threading.Tasks;
class Program
{
static AsyncExecutionLock _lock = new();
static async Task Main()
{
await NotUseLock();
await UseLock();
}
private static async Task UseLock()
{
int counter = 0;
var asyncLock = new AsyncExecutionLock();
List<Task> tasks = new();
for (int i = 0; i < 1000; i++)
{
tasks.Add(
Task.Run(() =>
{
// 👇👇👇👇👇👇👇👇👇
asyncLock.Execute(() =>
{
counter++;
});
})
);
}
await Task.WhenAll(tasks);
Console.WriteLine($"最终结果(有锁):{counter}"); // 结果等于1000
}
private static async Task NotUseLock()
{
int counter = 0;
List<Task> tasks = new();
for (int i = 0; i < 1000; i++)
{
tasks.Add(
Task.Run(() =>
{
// 多个线程同时修改 counter,可能导致丢失更新
counter++;
})
);
}
await Task.WhenAll(tasks);
Console.WriteLine($"最终结果(无锁):{counter}"); // 结果通常小于1000
}
}
MutextLock\AsyncExecutionLock.cs
public class AsyncExecutionLock
{
// 声明一个信号量,初始计数为1,最大计数为1,相当于一个“异步互斥锁”
private readonly SemaphoreSlim _semaphore = new(1, 1);
// 同步执行:获得锁后执行action,执行完毕后释放锁
public void Execute(Action action)
{
_semaphore.Wait(); // 阻塞直到获得信号量
try
{
action.Invoke(); // 执行传入的同步委托
}
finally
{
_semaphore.Release(); // 无论是否异常都释放信号量
}
}
// 异步执行:获得锁后执行异步action,执行完毕后释放锁
public async Task ExecuteAsync(Func<Task> action)
{
await _semaphore.WaitAsync(); // 异步等待获得信号量
try
{
await action.Invoke(); // 执行传入的异步委托
}
finally
{
_semaphore.Release(); // 无论是否异常都释放信号量
}
}
// 异步执行并返回结果:获得锁后执行异步action,返回结果,执行完毕后释放锁
public async Task<TReturn> ExecuteAsync<TReturn>(Func<Task<TReturn>> action)
{
await _semaphore.WaitAsync(); // 异步等待获得信号量
try
{
return await action.Invoke(); // 执行传入的异步委托并返回结果
}
finally
{
_semaphore.Release(); // 无论是否异常都释放信号量
}
}
}
MutextLock\MutextLock.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>