这种场景也是经常碰到的场景,例如,两个异步相互协作完成一项任务,一个任务让多个异步任务顺序执行等等吧。
在异步中不要用lock,这一定要记住
第一种方式是应用社区中的AsyncLock 的异步锁机制,在异步编程中常用的工具,用于控制对共享资源的并发访问
private async Task Test()
{
Nito.AsyncEx.AsyncLock mutex = new Nito.AsyncEx.AsyncLock();
var start = DateTime.Now;
var tasks = Enumerable.Range(1, 10).Select(x => ComputeAsync(x, mutex)).ToList();
var results = await Task.WhenAll(tasks);
CommonFun.WriteLine(string.Join(",", results));
var end = DateTime.Now;
CommonFun.WriteLine($"{(end - start).TotalMilliseconds}. ms");
}
private async Task<int> ComputeAsync(int x, Nito.AsyncEx.AsyncLock mutex)
{
using (await mutex.LockAsync())// 异步等待获取锁
{
await Task.Delay(100);// 不会阻塞线程
return x * x;
}
}
上面代码中的10 个任务会串行执行,而不是并行,每个任务需要等待前一个任务释放锁
第二种方法是SemaphoreSlim,.net原生提供的,SemaphoreSlim 是 .NET 中用于控制并发访问的轻量级信号量类,特别适合异步编程场景
- 信号量:控制同时访问特定资源的线程数量
- 轻量级:比传统的 Semaphore 性能更好
- 异步支持:提供 WaitAsync() 方法
SemaphoreSlim semaphore = new SemaphoreSlim(initialCount: 1);
private async void Test()
{
var start = DateTime.Now;
var tasks = Enumerable.Range(1, 10).Select(x => ComputeAsync2(x)).ToList();
var results = await Task.WhenAll(tasks);
CommonFun.WriteLine(string.Join(",", results));
var end = DateTime.Now;
CommonFun.WriteLine($"Elapsed:{(end - start).TotalMilliseconds:F4} ms");
}
private async Task<int> ComputeAsync2(int x)
{
await semaphore.WaitAsync();//异步等待
await Task.Delay(100);
semaphore.Release();//释放
return x * x;
}
第三种是AsyncAutoResetEvent,AsyncAutoResetEvent 是一个异步版本的自动重置事件,常用于异步编程中的线程同步
- 自动重置:当事件被设置后,只有一个等待的线程/任务会被释放,然后事件自动重置为非信号状态
- 异步支持:提供 WaitAsync() 方法,不会阻塞线程
- 轻量级:比传统的 AutoResetEvent 更适合异步场景
应用场景 1. 生产者-消费者模式 2. 异步任务协调 3. 资源池管理 4. 异步批处理 5. 异步状态机
靠Set(设置事件(释放一个等待的任务))和WaitAsync(等待事件), 两个事件来控制
简单的示例
private async void Test14()
{
var signal = new AsyncAutoResetEvent(set: false);
var setter = Task.Run(() =>
{
Thread.Sleep(millisecondsTimeout: 2000);
signal.Set();
CommonFun.WriteLine("Send!");
});
var waiter = Task.Run(async () =>
{
await signal.WaitAsync();
CommonFun.WriteLine("received!");
});
await Task.WhenAll(setter, waiter);
}
下面是一个生产者消费者示例
public class AsyncProducerConsumer<T>
{
private readonly AsyncAutoResetEvent _event = new AsyncAutoResetEvent();
private readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
private volatile bool _isCompleted = false;
public void Produce(T item)
{
_queue.Enqueue(item);
_event.Set(); // 通知消费者有数据可用
}
public async Task<T> ConsumeAsync(CancellationToken cancellationToken = default)
{
while (!_isCompleted)
{
if (_queue.TryDequeue(out var item))
{
return item;
}
// 等待生产者通知
await _event.WaitAsync(cancellationToken);
}
throw new InvalidOperationException("生产者已结束");
}
public void Complete()
{
_isCompleted = true;
_event.Set(); // 唤醒所有等待的消费者
}
}
使用如下
// 使用示例
var producerConsumer = new AsyncProducerConsumer<int>();
// 消费者任务
var consumerTask = Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
var item = await producerConsumer.ConsumeAsync();
Console.WriteLine($"消费: {item}");
await Task.Delay(100);
}
});
// 生产者任务
var producerTask = Task.Run(async () =>
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(200);
producerConsumer.Produce(i);
Console.WriteLine($"生产: {i}");
}
});
await Task.WhenAll(producerTask, consumerTask);

360

被折叠的 条评论
为什么被折叠?



