Динамический счетчик запросов семафоров - PullRequest
0 голосов
/ 02 июля 2018

У меня есть приложение 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() будет работать, идея состояла в том, чтобы отменить все ожидания, чтобы все потоки продолжались.

...