У меня есть приложение ASP. NET MVC5, использующее Ninject для DI. У меня есть сообщение, которое отображается вверху каждой страницы. Сообщение извлекается из веб-службы с помощью операции asyn c. Само сообщение небольшое и обновляется нечасто, поэтому я хочу кэшировать его в памяти.
Я создал простую службу, настроенную как синглтон DI. У меня была хорошая штука ReaderWriterLock
, но она не поддерживает asyn c. Итак, я попытался воссоздать то же самое с SemaphoreSlim
. Вот что у меня есть:
public class ExampleService {
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1);
private DateTime? lastAccess = null;
private string cachedMessage = null;
public async Task<string> GetMessageAsync() {
if (lastAccess.HasValue && lastAccess.Value > DateTime.UtcNow.AddHours(-1)) {
return cachedMessage;
}
var writeable = semaphore.CurrentCount == 1;
await semaphore.WaitAsync();
try {
if (writeable) {
// Do async stuff
}
}
finally {
semaphore.Release();
}
return cachedMessage;
}
~ExampleService() {
if (semaphore != null) semaphore.Dispose();
}
}
Цель состоит в том, чтобы все вызовы ждали, пока cacheMessage
не заполнится, а затем передать его всем. Проблема с моим решением заключается в том, что после завершения вызова записи все ожидающие чтения фактически застревают в очереди, освобождаясь одно за другим, в то время как новые чтения полностью пропускают очередь.
Что лучше способ сделать это?
Обновление
Основываясь на этом сообщении , многие люди, похоже, выступают за использование LazyCache . Делаем реализацию:
public class ExampleService {
private readonly IAppCache cache;
public ExampleService(IAppCache cache) {
this.cache = cache;
}
private async Task<string> GetMessageFromServerAsync() {
// result = async stuff
return result;
}
public async Task<string> GetMessageAsync() {
return await cache.GetOrAddAsync("MessageKey", GetMessageFromServerAsync);
}
}