我的问题是: 我正在从别人的API接收过期令牌.我的任务是仅在我设置的时间请求新令牌(例如,每1分钟一次).我的服务同时收到数百个请求.到了刷新令牌的时候,我的代码请求100个新令牌. 当我做一个测试时,首先请求一个令牌,然后并行请求多个令牌时,返回的是已经创建的令牌.但如果我立刻try 同时发出一百个请求,那么每个人都会收到一个新的令牌. 如何最好地处理这项任务?现在是这样的.

_timeGetToken次接收令牌 tokenUpdateTime—需要更新令牌的时间(双倍,分钟(1))

public async Task<string> GetToken()
{
    if(DateTime.Now.Subtract(_timeGetToken) > TimeSpan.FromMinutes(tokenUpdateTime))
    {
        try
        {
            token = await _api.GetToken(new TokenUser());
            _timeGetToken = DateTime.Now;
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex.Message);
        }
    }
    if(token != null) 
        return token.Token;
    return string.Empty;
}

我的测试:

var t1 = await tokenHandler.GetToken();

List<Task<string>> tokensTasks = new();
for (int i = 0; i < 100; i++)
    tokensTasks.Add(tokenHandler.GetToken());

var tokens = await Task.WhenAll(tokensTasks);

如果我这样做,我会得到同样的 token , 但如果我删除第一个令牌请求,我将得到一百个不同的令牌

//var t1 = await tokenHandler.GetToken();

List<Task<string>> tokensTasks = new();
for (int i = 0; i < 100; i++)
    tokensTasks.Add(tokenHandler.GetToken());

var tokens = await Task.WhenAll(tokensTasks);

一开始我决定使用信号量是值得的,但很明显,在这段代码中,它们不会以任何方式帮助我,我的意思是我需要以不同的方式计算令牌的生命周期或以不同的方式存储它. 提前感谢任何建议

推荐答案

为了防止并发环境中同时刷新多个令牌,可以使用SemaphoreSlim作为异步锁.这确保了每次只有一个线程可以执行令牌刷新代码.以下是如何将SemaphoreSlim集成到GetToken方法中,以及双重判断的锁定模式:

private readonly SemaphoreSlim _tokenRefreshLock = new SemaphoreSlim(1, 1);

public async Task<string> GetToken()
{
    // First check to see if the token needs refreshing
    if (DateTime.Now.Subtract(_timeGetToken) > TimeSpan.FromMinutes(tokenUpdateTime))
    {
        bool acquiredLock = false;
        try
        {
            // Acquire the lock
            await _tokenRefreshLock.WaitAsync();
            acquiredLock = true;

            // Double-check to see if another task has already refreshed the token
            if (DateTime.Now.Subtract(_timeGetToken) > TimeSpan.FromMinutes(tokenUpdateTime))
            {
                token = await _api.GetToken(new TokenUser());
                _timeGetToken = DateTime.Now;
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex.Message);
        }
        finally
        {
            // Ensure the lock is released
            if (acquiredLock)
            {
                _tokenRefreshLock.Release();
            }
        }
    }

    return token != null ? token.Token : string.Empty;
}

该方法显著降低了多个线程同时进入令牌刷新逻辑的可能性,因为第一判断可以防止大多数不必要的锁获取,并且在获取锁之后完成的第二判断确保即使多个线程通过第一判断也只发生一次刷新.

Csharp相关问答推荐

PostingAsJsonAschange在从调用的方法返回时给出500错误

在Microsoft XNA框架(MonoGame)中旋转相机

在WPF.NET 6中使用C++/WinRT组件(但实际上是任何WinRT组件)

在命令行中使用时安装,但在单击时不会安装

如何在Reflection. Emit中使用具有运行时定义的类型参数的泛型类型

如何将Kafka消息时间戳转换为C#中的日期和时间格式?

返回TyedResults.BadRequest<;字符串>;时问题详细信息不起作用

发布用于Linux Ubuntu的C#应用程序

用C#调用由缓冲区指针参数组成的C API

为什么无法将对象转换为泛型类型

在不添加不必要的尾随零的情况下本地化浮点型?

在字符串C#之前获取数字

同一组件的多个实例触发相同的事件处理程序

如何避免在.NET中将日志(log)写入相对路径

WPF动态设置弹出窗口水平偏移

如何在特定环境中运行dotnet测试?

流畅的验证--如何为属性重用规则?

WPF:如何从DatagridHeader的内容模板绑定到词典项

反序列化我以前使用System.Text.Json序列化的文件时出现异常

在C#中删除多个不同名称的会话