asp 请求的线程安全缓存 object
Thread safe cached object for asp request
首先,我不能让标题更说明问题,我会尝试列出问题然后提供我的解决方案
我正在 asp 核心中为我们的游戏实现一个后端,我们几乎没有多少请求有点大,比如请求我们在商店中提供的物品,每个用户启动游戏都会加载商店信息这使得数据库访问以提取整个商店信息,这些信息很少更改 - 每月少于一次 - 因此我们正在进行数千次不需要的数据库访问。
最重要的是,我们 return 时间戳表示项目图像最后一次更改的时间,图像存储在一个 blob 中,这让我查询 blob 的更改日期,这使得请求方式更昂贵
所以为了解决所有这些问题,我实现了一个小的 class 来缓存请求,直到我们需要更新它,对于这个请求和其他一些,但我不确定我是否正在寻找在这正确
这里是基础摘要class:
public abstract class CachedModel<T>
{
protected T Model { get; set; }
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1,1);
protected abstract Task ThreadSafeUpdateAsync();
protected abstract bool NeedsUpdate();
public async Task<T> GetModel()
{
if (NeedsUpdate())
{
try
{
await semaphore.WaitAsync();
if(NeedsUpdate()) // not sure if this is needed, can other threads enter here after the first one already updated the object?
await ThreadSafeUpdateAsync();
}
finally
{
semaphore.Release();
}
}
return Model;
}
}
然后我按照这样的请求实现这个 class:
public class CachedStoreInfo : CachedModel<DesiredModel>
{
protected override async Task ThreadSafeUpdateAsync()
{
// make the trip to db and Blob service
Model = some result
}
protected override bool NeedsUpdate()
{
return someLogicToDecideIfNeedsUpdate;
}
}
最后,在 asp 控制器中,我需要做的就是:
[HttpGet]
public async Task<DesiredModel> GetStoreInfo()
{
return await cachedStoreInfo.GetModel();
}
这是正确的实施方式吗?这是必要的还是有更聪明的方法来实现这一目标?从 blob 获取时间戳是我缓存结果的主要原因
您的实施看起来是正确的。当然 CachedStoreInfo
的实例应该是所需范围内的单例(据我了解,在您的情况下它应该是应用范围内的单例)。
can other threads enter here after the first one already updated the object?
正如 Kevin Gosse 指出的那样,其他主题可以进入此处。您对 NeedsUpdate()
的第二次检查是 Double-checked locking 模式的一部分。这可能是一个很好的优化。
and is this even necessary or there is a smarter way to achieve this?
对我来说,你的实现是极简主义的并且足够聪明
首先,我不能让标题更说明问题,我会尝试列出问题然后提供我的解决方案
我正在 asp 核心中为我们的游戏实现一个后端,我们几乎没有多少请求有点大,比如请求我们在商店中提供的物品,每个用户启动游戏都会加载商店信息这使得数据库访问以提取整个商店信息,这些信息很少更改 - 每月少于一次 - 因此我们正在进行数千次不需要的数据库访问。
最重要的是,我们 return 时间戳表示项目图像最后一次更改的时间,图像存储在一个 blob 中,这让我查询 blob 的更改日期,这使得请求方式更昂贵
所以为了解决所有这些问题,我实现了一个小的 class 来缓存请求,直到我们需要更新它,对于这个请求和其他一些,但我不确定我是否正在寻找在这正确
这里是基础摘要class:
public abstract class CachedModel<T>
{
protected T Model { get; set; }
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1,1);
protected abstract Task ThreadSafeUpdateAsync();
protected abstract bool NeedsUpdate();
public async Task<T> GetModel()
{
if (NeedsUpdate())
{
try
{
await semaphore.WaitAsync();
if(NeedsUpdate()) // not sure if this is needed, can other threads enter here after the first one already updated the object?
await ThreadSafeUpdateAsync();
}
finally
{
semaphore.Release();
}
}
return Model;
}
}
然后我按照这样的请求实现这个 class:
public class CachedStoreInfo : CachedModel<DesiredModel>
{
protected override async Task ThreadSafeUpdateAsync()
{
// make the trip to db and Blob service
Model = some result
}
protected override bool NeedsUpdate()
{
return someLogicToDecideIfNeedsUpdate;
}
}
最后,在 asp 控制器中,我需要做的就是:
[HttpGet]
public async Task<DesiredModel> GetStoreInfo()
{
return await cachedStoreInfo.GetModel();
}
这是正确的实施方式吗?这是必要的还是有更聪明的方法来实现这一目标?从 blob 获取时间戳是我缓存结果的主要原因
您的实施看起来是正确的。当然 CachedStoreInfo
的实例应该是所需范围内的单例(据我了解,在您的情况下它应该是应用范围内的单例)。
can other threads enter here after the first one already updated the object?
正如 Kevin Gosse 指出的那样,其他主题可以进入此处。您对 NeedsUpdate()
的第二次检查是 Double-checked locking 模式的一部分。这可能是一个很好的优化。
and is this even necessary or there is a smarter way to achieve this?
对我来说,你的实现是极简主义的并且足够聪明