我的问题: 我从其他人的 API 收到过期令牌。我的任务是仅在我设置的时间请求新令牌(例如,每 1 分钟一次)。我的服务同时收到数百个请求。当刷新令牌时,我的代码请求一百个新令牌。 当我进行测试时,我首先请求一个令牌,然后并行请求多个令牌,我会返回已创建的令牌。但如果我立即尝试同时发出一百个请求,那么每个人都会收到一个新令牌。 如何最好地应对任务?现在是这样的。
_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);
如果我这样做,我会得到同样的令牌, 但如果我删除第一个令牌请求,我将获得一百个不同的令牌
//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;
}
这种方法显着降低了多个线程同时进入令牌刷新逻辑的可能性,因为第一次检查可能会阻止大多数不必要的锁获取,而第二次检查在获取锁后完成,可确保即使多个线程通过,也只会发生一次刷新第一次检查。