c# - 阻止并发调用者并从异步方法返回单个结果

标签 c# concurrency async-await

我有一个由 ApiClient 类的多个使用者同时调用的方法。我想阻止对该方法的并发调用,直到第一个调用完成,然后短路其余的调用。

在下面的伪代码中,多个线程可能调用RefreshApiTokenAsync()。我想阻止除一次调用内部 this.GetNewApiTokenAsync() 方法之外的所有调用。这将避免代码在多个线程上检测到过期的 ApiToken,然后尝试多次刷新它的情况。

public class ApiClient
{
    private static readonly ConcurrentDictionary<string, string> ApiTokens = new ConcurrentDictionary<string, string>();

    public async Task DoSomething()
    {
        // Call third party API and then detect an out of date API token.
        // The CallThirdPartyApi uses the token in the ApiTokens ConcurrentDictionary
        var result = await CallThirdPartyApi();

        if (result.ApiTokenOutOfDate) {
            await this.RefreshApiTokenAsync();
            result = await CallThirdPartyApi();                
        }

        return result;
    }

    private async Task<string> RefreshApiTokenAsync()
    {
        string newToken = await this.GetNewApiTokenAsync();
        return ApiTokens.AddOrUpdate("ApiToken", newToken, (key, value) => newToken);
    }
}

我相信这被认为是去抖动,但我不确定如何实现这一点。

最佳答案

您可以存储刷新 token 任务并将其返回给调用者。 token 刷新后,调用者可以继续执行。这是示例任务存储:

private static readonly ConcurrentDictionary<string, Lazy<Task>> RefreshTokenTasks = new ConcurrentDictionary<string, Lazy<Task>>();

刷新 token 方法可以是这样的:

private Task RefreshApiTokenAsync()
{
    return RefreshTokenTasks.GetOrAdd("refreshTokenTask", _ => new Lazy<Task>( async () =>
    {
        try
        {
            string newToken = await this.GetNewApiTokenAsync();
            ApiTokens.AddOrUpdate("ApiToken", newToken, (key, value) => newToken);
        }
        finally
        {
            Lazy<Task> refreshTask;
            RefreshTokenTasks.TryRemove("refreshTokenTask", out refreshTask);
        }
    }, LazyThreadSafetyMode.ExecutionAndPublication)).Value;
}

关于c# - 阻止并发调用者并从异步方法返回单个结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38401227/

相关文章:

javascript - 如何使用 async/await 将此回调转换为 promise?

c# - 中心段落文本

c# - 我的 C# 应用程序出现全屏错误

go - 为什么我在通过 channel 接收值时看不到输出

java - 何时通过扩展创建线程

javascript - async await 是否比普通 Promises 对性能要求更高?

c# - .NET Core Entity Framework - 为类库中的 Context 添加迁移

c# - 对 ASP.NET 自定义验证器进行排序以及验证器的 Text 和 ErrorMessage 属性之间的差异

java - HttpServletRequest.getServerName() 在并发使用中偶尔返回 null?

c# - 如何在不等待的情况下返回 Task<T>