当有多个调用调用该方法时,一种锁定等待调用的方法? [复制]
Posted
技术标签:
【中文标题】当有多个调用调用该方法时,一种锁定等待调用的方法? [复制]【英文标题】:A way to Lock await calls one at a time when there are multiple calls calling the method? [duplicate] 【发布时间】:2019-11-19 17:53:15 【问题描述】:我有一个供人们调用服务的 api 端点, 如果缓存已过期,我只希望 GetCacheToken 一次运行一个。
显然我通过getAccessToken同时有3个或更多调用getCacheToken,并且它同步处理(不知道为什么),当它等待_client.SendAsync(requestData);它将刷新刷新令牌,旧的刷新令牌将不再有效。
所以第一次调用通过,第二次和第三次失败,因为他们都使用旧令牌在发送请求中传递它。
有没有办法在缓存过期时锁定进程? 因为 Lock 不能用于 await 调用。 谢谢
认为它会被异步等待处理但没有运气
[System.Web.Http.HttpGet]
[System.Web.Http.Route("getAccessToken")]
public async Task<string> getAccessToken()
return await cacheToken.GetCacheToken();
private static readonly SemaphoreSlim _mutex = new SemaphoreSlim(1,1);
public async Task<string> GetCacheToken()
ObjectCache cache = MemoryCache.Default;
var cacheToken = cache.Get("refreshToken", null);
string refreshToken = cacheToken == null ? GetToken() : cacheToken.ToString();
if (!cache.Contains("apiToken"))
//it prevent multiple threads call to update refreshtoken at the same time
await _mutex.WaitAsync();
try
//Log.Information("Access token " + cache.Get("apiToken", null));
var isNull = cache.Get("refreshToken", null) == null ? "Yes " : "No ";
var logtime = DateTime.Now.ToString("O");
Log.Information("refresh token is null : " + isNull + DateTime.Now.ToString("O") + " " + logtime);
Log.Information(
"refresh token : " + refreshToken + " " + DateTime.Now.ToString("O") + " " + logtime);
var httpContent = new StringContent("", Encoding.UTF8, "application/x-www-form-urlencoded");
var dict = new Dictionary<string, string>();
dict.Add("grant_type", "refresh_token");
dict.Add("refresh_token", refreshToken);
var requestData = new HttpRequestMessage
Method = HttpMethod.Post,
RequestUri = new Uri("https://oauth2.sky.blackbaud.com/token"),
Content = new FormUrlEncodedContent(dict)
;
requestData.Headers.Authorization = new AuthenticationHeaderValue("Basic", Settings.BasicAuth);
var results = await _client.SendAsync(requestData);
Log.Information("run time " + DateTime.Now);
var resultResponse = results.Content.ReadAsStringAsync().Result;
try
results.EnsureSuccessStatusCode();
var result = _js.Deserialize<TokenModel>(resultResponse);
//token expires in one hour from blackbaud
var expiration = DateTimeOffset.Now.AddMinutes(55);
cache.Set("apiToken", result.access_token, expiration);
cache.Set("refreshToken", result.refresh_token, expiration);
await UpdateToken(result.access_token, result.refresh_token);
Log.Information("refresh token after update : " + cache.Get("refreshToken", null) +
DateTime.Now.ToString("O"));
catch (Exception e)
var exceptionMessage = $"ResultMessage : resultResponse Exception: e.";
Log.Exception(e, exceptionMessage);
throw;
finally
_mutex.Release();
return cache.Get("apiToken", null).ToString();
这是日志
12 Jul 2019 12:00:10.847
ResultMessage : "error":"invalid_grant" Exception: System.Net.Http.HttpRequestException: Response status code does not indicate success: 400 (Bad Request). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at RaisersEdge.Infrastructure.Cache.<GetCacheToken>d__3.MoveNext().Exception: HttpRequestException "Message":"Response status code does not indicate success: 400 (Bad Request).","Data":[],…Stack: at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at RaisersEdge.Infrastructure.Cache.<GetCacheToken>d__3.MoveNext()Category: Exception
12 Jul 2019 12:00:10.841
run time 12/07/2019 12:00:10 PM
12 Jul 2019 12:00:09.981
refresh token : 03124379add64a90850961e6a2021d6e 2019-07-12T12:00:09.9813412+12:00 2019-07-12T12:00:09.9813412+12:00
12 Jul 2019 12:00:09.981
refresh token is null : No 2019-07-12T12:00:09.9813412+12:00 2019-07-12T12:00:09.9813412+12:00
12 Jul 2019 12:00:09.980
refresh token after update : f0b569cfa2254bbfbd78e9d84ddd66ae2019-07-12T12:00:09.9803523+12:00
12 Jul 2019 12:00:09.951
run time 12/07/2019 12:00:09 PM
12 Jul 2019 12:00:08.970
refresh token : 03124379add64a90850961e6a2021d6e 2019-07-12T12:00:08.9212759+12:00 2019-07-12T12:00:08.9192713+12:00
12 Jul 2019 12:00:08.942
refresh token is null : Yes 2019-07-12T12:00:08.9192713+12:00 2019-07-12T12:00:08.9192713+12:00
【问题讨论】:
你会在这里找到原因和可能的解决方案***.com/questions/7612602/… 【参考方案1】:lock
的异步等效项是 SemaphoreSlim
。
如果您想要的代码如下所示:
private readonly object _mutex = new object();
...
lock (_mutex)
...
var results = await _client.SendAsync(requestData);
...
然后您可以使用SemaphoreSlim
来获得所需的行为:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim();
...
await _mutex.WaitAsync();
try
...
var results = await _client.SendAsync(requestData);
...
finally
_mutex.Release();
【讨论】:
信号量不应该是静态的吗? @xxbbcc:在这种情况下,是的。 感谢您的回复,只是想知道以这种方式这样做是一种好习惯吗? 如果你真的必须阻止其他调用,那么可以。 抱歉,它仍然无法正常工作,我已设置为 new SemaphoreSlim(1,1);一次一个,并尝试使用 1,3,但不起作用。2 个呼叫在日志中同时通过,一个在 2019 年 12:00:09 另一个在 2019 年 12:00:08以上是关于当有多个调用调用该方法时,一种锁定等待调用的方法? [复制]的主要内容,如果未能解决你的问题,请参考以下文章