具有异步操作的线程安全单例
Thread safe singleton with async operation
我有一个 ASP.NET MVC5 应用程序使用 Ninject 作为 DI。我有一条消息显示在每一页的顶部。使用异步操作从 Web 服务检索消息。消息本身很小,更新不频繁,所以想缓存在内存中。
我创建了一个配置为 DI 单例的简单服务。 ReaderWriterLock
我有一件好事,但它不支持异步。所以我尝试用 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
被填充,然后将其分配给所有人。我的解决方案的问题是,一旦写入调用完成,所有等待的读取实际上都被困在一个队列中,一个接一个地被释放,而新的读取则完全跳过队列。
执行此操作的更好方法是什么?
更新
基于此post, a lot of people appear to advocate just using 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);
}
}
基于此post, a lot of people appear to advocate just using 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);
}
}
我有一个 ASP.NET MVC5 应用程序使用 Ninject 作为 DI。我有一条消息显示在每一页的顶部。使用异步操作从 Web 服务检索消息。消息本身很小,更新不频繁,所以想缓存在内存中。
我创建了一个配置为 DI 单例的简单服务。 ReaderWriterLock
我有一件好事,但它不支持异步。所以我尝试用 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
被填充,然后将其分配给所有人。我的解决方案的问题是,一旦写入调用完成,所有等待的读取实际上都被困在一个队列中,一个接一个地被释放,而新的读取则完全跳过队列。
执行此操作的更好方法是什么?
更新
基于此post, a lot of people appear to advocate just using 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);
}
}
基于此post, a lot of people appear to advocate just using 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);
}
}