Поточно-безопасный синглтон с операцией asyn c - PullRequest
0 голосов
/ 07 мая 2020

У меня есть приложение 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);
        }

    }

1 Ответ

0 голосов
/ 19 мая 2020

Основываясь на этом сообщении , многие люди, похоже, выступают за использование 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);
    }

}
...