ASP请求的线程安全缓存对象

时间:2019-01-25 19:30:04

标签: c# asp.net semaphore

首先,我无法使标题更具解释性,我将尝试列出问题,然后为其提供解决方案

我正在为我们的游戏在asp核心中实现一个后端,我们收到的请求有些大,比如请求商店中提供的物品,每个用户启动游戏都会加载商店信息,从而使数据库之旅提取几乎很少变化的整个商店信息-每月少于一次-因此我们正在进行数千次不需要的数据库旅行。

最重要的是,我们返回时间戳,表示商品图像的最后一次更改时间,图像存储在blob中,这使我可以查询blob的更改日期,从而使请求的方式更加昂贵

因此,为了解决所有这些问题,我实现了一个小类来缓存请求,直到需要针对该请求和其他请求进行更新为止,但是我不确定我是否正确地查看了该请求

这是基本的抽象类:

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;
    }
}

然后我按照每个请求实现此类:

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获取时间戳是我虽然要缓存结果的主要原因

1 个答案:

答案 0 :(得分:0)

您的实现看起来正确。当然,CachedStoreInfo的实例在所需范围内应为单例(据我所知,在应用程序范围内应为单例)。

  

第一个已经更新对象后,其他线程可以进入这里吗?

正如Kevin Gosse指出的,其他线程可以在此处输入。 NeedsUpdate()的第二张支票是Double-checked locking模式的一部分。这可能是一个很好的优化。

  

这甚至是必要的还是有一种更聪明的方法来实现这一目标?

对于我来说,您的实现非常简单,足够聪明