依赖库
Nito.AsyncEx;
完整代码
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Nito.AsyncEx;
namespace DTcms.Common
{
public static class AsyncLockHelper
{
/// <summary>
/// 异步锁
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static Task<IDisposable> LockAsync(object key)
{
return GetLock(key).LockAsync();
}
/// <summary>
/// 同步锁
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static IDisposable Lock(object key)
{
return GetLock(key).Lock();
}
/// <summary>
/// 获取一个异步锁对象
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static AsyncLock GetLock(object key)
{
return _locks.GetOrAdd(key, _ => new Lazy<AsyncLock>(() => new AsyncLock())).Value;
}
/// <summary>
/// 手动添加一个异步锁对象
/// </summary>
/// <param name="key"></param>
/// <param name="asyncLock"></param>
/// <returns></returns>
public static AsyncLock AddLock(object key, AsyncLock asyncLock)
{
return _locks.AddOrUpdate(key, _ => new Lazy<AsyncLock>(() => asyncLock),
(_, __) => new Lazy<AsyncLock>(() => asyncLock)).Value;
}
/// <summary>
/// 手动添加一个异步锁对象
/// </summary>
/// <param name="key"></param>
/// <param name="asyncLock"></param>
/// <returns></returns>
public static AsyncLock AddLock(object key, Func<AsyncLock> createAsyncLock)
{
return _locks.AddOrUpdate(key, _ => new Lazy<AsyncLock>(createAsyncLock),
(_, __) => new Lazy<AsyncLock>(createAsyncLock)).Value;
}
public static Lazy<AsyncLock> RemoveLock(object key)
{
_locks.TryRemove(key, out var lazy);
return lazy;
}
/// <summary>
/// 异步锁字典
/// </summary>
private static ConcurrentDictionary<object, Lazy<AsyncLock>> _locks =
new ConcurrentDictionary<object, Lazy<AsyncLock>>();
}
}
并发示例
// [TestMethod]
public async Task testWhenAll()
{
var sb = new StringBuilder();
var watch = ValueStopwatch.StartNew();
await Task.WhenAll(Enumerable.Range(0, 5).Select(async i =>
{
await Task.Delay(i * 1000); // 模拟异步操作
using (await AsyncLockHelper.LockAsync(sb))
{
await Task.Delay(i * 1000); // 模拟异步操作
sb.AppendLine($"[{watch.GetElapsedTime()}] i: " + i);
}
}));
var use_time = watch.GetElapsedTime(); // 总耗时
}
ValueStopWatch.cs
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// https://source.dot.net/#Microsoft.AspNetCore.Mvc.Core/src/Shared/ValueStopwatch/ValueStopwatch.cs
using System;
using System.Diagnostics;
namespace DTcms.Common
{
public struct ValueStopwatch
{
#if !NET7_0_OR_GREATER
private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
#endif
private readonly long _startTimestamp;
public readonly DateTime StartTime;
public bool IsActive => _startTimestamp != 0;
private ValueStopwatch(long startTimestamp)
{
StartTime = DateTime.Now;
_startTimestamp = startTimestamp;
}
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
public TimeSpan GetElapsedTime()
{
// Start timestamp can't be zero in an initialized ValueStopwatch. It would have to be literally the first thing executed when the machine boots to be 0.
// So it being 0 is a clear indication of default(ValueStopwatch)
if (!IsActive)
{
throw new InvalidOperationException(
"An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.");
}
var end = Stopwatch.GetTimestamp();
#if !NET7_0_OR_GREATER
var timestampDelta = end - _startTimestamp;
var ticks = (long)(TimestampToTicks * timestampDelta);
return new TimeSpan(ticks);
#else
return Stopwatch.GetElapsedTime(_startTimestamp, end);
#endif
}
}
}