异步中实现同步机制

这种场景也是经常碰到的场景,例如,两个异步相互协作完成一项任务,一个任务让多个异步任务顺序执行等等吧。
在异步中不要用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 中用于控制并发访问的轻量级信号量类,特别适合异步编程场景

  1. 信号量:控制同时访问特定资源的线程数量
  2. 轻量级:比传统的 Semaphore 性能更好
  3. 异步支持:提供 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 是一个异步版本的自动重置事件,常用于异步编程中的线程同步

  1. 自动重置:当事件被设置后,只有一个等待的线程/任务会被释放,然后事件自动重置为非信号状态
  2. 异步支持:提供 WaitAsync() 方法,不会阻塞线程
  3. 轻量级:比传统的 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);
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值