У меня есть приложение ASP.NET, которое использует кэш для вызова API, который необходим только для его создания.
В настоящий момент, если я получаю несколько запросов, все они в конечном итоге обращаются к API, а не читают кэш, потому что для возврата запроса требуется некоторое время. Так эффективно я получаю пропуск кеша, поэтому, чтобы предотвратить это, я сделал:
class GetData
{
private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim( 1, 1 );
public async Task<ReferenceDataModel> GetReferenceDataAsync()
{
this.logger.Trace( "About to hit Reference Data lock" );
await SemaphoreSlim.WaitAsync( );
this.logger.Trace( "Acquired Thread" );
var referenceDataModel = await this.GetReferenceDataImplAsync();
this.logger.Trace( "Releasing Thread" );
SemaphoreSlim.Release( 1 );
return referenceDataModel;
}
private async Task<ReferenceDataModel> GetReferenceDataImplAsync()
{
if ( this.cache.Contains( ReferenceDataCacheKey ) )
{
this.logger.Trace( "Returning Cached Response" );
return (ReferenceDataModel) this.cache.Get( ReferenceDataCacheKey );
}
this.logger.Trace( "Making Reference Data API call" );
var response = await this.referenceDataProvider.GetReferenceDataAsync().ConfigureAwait( false );
this.AddReferenceDataToCache( response );
return response;
}
private void AddReferenceDataToCache(ReferenceDataModel mappedResponse)
{
var currentDateTime = DateTime.UtcNow;
var expirationOffset = currentDateTime.TimeOfDay < this.cacheExpiryTime
? currentDateTime.Date.Add(this.cacheExpiryTime)
: currentDateTime.AddDays(1).Date.Add(this.cacheExpiryTime);
this.cache.Set(ReferenceDataCacheKey, mappedResponse, expirationOffset);
this.logger.Trace( "Cached API Call Response" );
}
}
Это работает, блокируя каждый запрос, как только один запрос получил блокировку, однако, как мьютекс, он позволит один за другим получить доступ к кешу ...
Таким образом, идеальным поведением было бы то, что каждый запрос удерживается WaitAsync()
, а затем, как только первый запрос фактически разрешил запрос API, пусть каждый запрос продолжается (поскольку все они попадут в кеш).
Я подумал, что, возможно, с помощью CancellationToken
с Semaphore.WaitAsync()
будет работать, идея состояла в том, чтобы отменить все ожидания, чтобы все потоки продолжались.