并发缓存处理

时间:2014-05-15 22:33:30

标签: c# concurrency httprequest

我觉得自己已经碰到了一堵砖墙而又无法再深入了解,可能一直在思考它。

我正在创建一个缓存处理类,它从数据库中获取一些数据,检查它的时间,然后如果它已经过时它将从第三方API请求新数据。

但我坚持并发。第三方API不喜欢太多的重复调用,因此在Get()被同时调用两次并且都试图更新数据的不幸事件中,这是一个不幸的结果,我想删除它可能性。

但我也希望能够处理不同的路径,同时阻止其他路径尝试更新,而一条路径已经在获取数据。

public class CacheHandler : ICacheHandler
{
    private IRequester Requester;
    private ICache Cache;

    public CacheHandler()
    {
        Requester = new RequesterHttp();
        Cache = new CacheDB();
    }

    public T Get<T>(string path)
    {
        // Get data from cache
        var cacheRes = Cache.Get(path);

        // If not null, deserialize to object and compare if its time to update from third party or to return cached version
        if (cacheRes != null)
        {
            // Deserialize xml from db
            Response<T> deserialized = Xml.Deserialize<Response<T>>(cacheRes);
            // Compare xml from db cached untill with current time 
            int compare = DateTime.Compare(DateTime.UtcNow, deserialized.CachedUntil);

            // If cacheduntill is later than current time, return cached info
            if (compare <= 0)
                return deserialized.Result;
        }

        // If no cache, or its time to update current cache - Get third party xml
        var reqRes = Requester.Get(path);
        // Save it to cache
        Cache.Set(path, reqRes);

        // Desearialize
        Response<T> deserialized2 = Xml.Deserialize<Response<T>>(reqRes);

        // Return it to user
        return deserialized2.Result;
    }
}

但我的问题怎么样?

我想我可以将它全部合并到CacheHandler并对数据库执行悲观并发,但这会破坏将来能够扩展的想法。

1 个答案:

答案 0 :(得分:0)

在最基本的级别,您可以将此调用包装在一个锁中,以防止您描述的竞争条件:

public class CacheHandler : ICacheHandler
{

private IRequester Requester;
private ICache Cache;

private object syncLock = new object();


public CacheHandler()
{
    Requester = new RequesterHttp();
    Cache = new CacheDB();
}

public T Get<T>(string path)
{
    lock(syncLock)
    {
        // Get data from cache
        var cacheRes = Cache.Get(path);

        // If not null, deserialize to object and compare if its time to update from third party or to return cached version
        if (cacheRes != null)
        {
            // Deserialize xml from db
            Response<T> deserialized = Xml.Deserialize<Response<T>>(cacheRes);
            // Compare xml from db cached untill with current time 
            int compare = DateTime.Compare(DateTime.UtcNow, deserialized.CachedUntil);

            // If cacheduntill is later than current time, return cached info
            if (compare <= 0)
                return deserialized.Result;
        }

        // If no cache, or its time to update current cache - Get third party xml
        var reqRes = Requester.Get(path);
        // Save it to cache
        Cache.Set(path, reqRes);

        // Desearialize
        Response<T> deserialized2 = Xml.Deserialize<Response<T>>(reqRes);

        // Return it to user
        return deserialized2.Result;
    }
}
}

然而,从多种意义上说,这不是速度的最佳策略。理想情况下,您可能希望将此缓存数据存储在内存中,然后异步将其保存到数据库中。这样,您的碰撞窗口会变小(尽管不会完全消失)。

我没有看到围绕该方法的锁的方法,但如果你利用内存缓存,你将锁定一个更短的时间,而不是整个缓慢的从数据库往返(相比之下)。