You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步方法重叠执行优化:确保耗时任务仅单次执行

解决异步方法重叠执行的问题

这个场景我太熟悉了——当异步方法被频繁调用,而它的执行时长超过调用间隔时,就会出现多个任务并行执行,重复跑那些耗时操作。咱们的目标很明确:让所有与首次调用重叠的后续调用,都等待同一个正在执行的任务完成,而不是各自启动新的任务。

核心思路

我们需要一个「共享任务」的机制:

  • 用一个字段跟踪当前正在执行的异步任务
  • 每次调用方法时,先检查是否有正在运行的任务:有就直接等待它;没有就创建新任务,并让后续调用都共享这个任务
  • 用锁来处理并发场景下的任务赋值,避免竞态条件

实现代码

基于你给出的示例,修改后的代码如下:

public class AsyncTaskThrottler
{
    // 锁对象:保证多线程下访问_currentTask的线程安全
    private static readonly object _lockObj = new object();
    // 跟踪当前正在执行的异步任务
    private static Task _currentTask;

    public static void MethodA() { Console.Write("A"); }
    public static Task MethodB() { Console.Write("B"); return Task.Delay(500); }
    public static void MethodC() { Console.Write("C"); }

    public static async Task MethodAsync()
    {
        // 先检查是否有正在执行的任务,如果有直接等待
        var existingTask = _currentTask;
        if (existingTask != null)
        {
            await existingTask;
            return;
        }

        // 没有正在执行的任务,加锁创建新任务
        lock (_lockObj)
        {
            // 双重检查:防止在等待锁的过程中,其他线程已经创建了任务
            existingTask = _currentTask;
            if (existingTask != null)
            {
                await existingTask;
                return;
            }

            // 创建并保存当前任务,注意这里不能直接await,要先赋值再执行
            _currentTask = ExecuteCoreAsync();
            await _currentTask;
        }
    }

    // 封装实际的业务逻辑
    private static async Task ExecuteCoreAsync()
    {
        try
        {
            MethodA();
            await MethodB();
            MethodC();
        }
        finally
        {
            // 任务完成后,重置_currentTask,让下一次调用可以创建新任务
            lock (_lockObj)
            {
                _currentTask = null;
            }
        }
    }
}

代码解释

  1. 锁与双重检查:
    • 第一次检查_currentTask是为了快速路径,避免每次都进入锁
    • 进入锁后的第二次检查是关键:防止多个线程同时进入「创建任务」的分支,导致重复创建任务
  2. 任务跟踪与重置:
    • _currentTask保存当前正在执行的任务,所有重叠调用都会等待这个任务
    • 在ExecuteCoreAsync的finally块中重置_currentTask,确保无论任务成功还是失败,后续调用都能正常启动新任务
  3. 业务逻辑封装:把实际要执行的MethodA/MethodB/MethodC放到单独的ExecuteCoreAsync里,让核心逻辑和去重逻辑分离,更易维护

测试验证

如果你连续调用三次MethodAsync:

await Task.WhenAll(
    AsyncTaskThrottler.MethodAsync(),
    AsyncTaskThrottler.MethodAsync(),
    AsyncTaskThrottler.MethodAsync()
);

预期输出是ABC(只执行一次业务逻辑),而不是AAABBBCCC(重叠执行),完全符合你的需求。

注意事项

  • 如果MethodB可能抛出异常,finally块会保证_currentTask被重置,不会影响后续调用
  • 这个实现是静态类,适用于全局单例的场景;如果需要实例级别的去重,把_lockObj和_currentTask改成实例字段即可

内容的提问来源于stack exchange,提问作者Arokh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:49:50