5

我们有一个类似的 REST API 方法:

List<item> GetItems(int AccountID)
{
    var x = getFromCache(AccountID);
    if(x==null)
    {
        x = getFromDatabase(AccountID);
        addToCache(AccountID, x);
    }
    return x;
}

对于一些复杂的数据库调用,这是一种相当昂贵的方法,我们有一种常见的情况,数百名具有相同 AccountId 的用户几乎同时进行调用(他们都被广播通知)。

在该方法中,我们将结果集缓存 10 秒,因为对于在该窗口内发出请求的每个人来说,接近时间的结果都很好。但是,由于他们都同时进行调用(同样,对于特定的 AccountID),缓存永远不会预先填充,因此每个人最终都会进行数据库调用。

所以我的问题是,在该方法中,如何暂停针对特定 accountId 的所有传入请求并让它们都等待第一个结果集完成,以便其余调用可以使用缓存的结果集?

我已经阅读了一些关于 Monitor.Pulse 和 Monitor.Lock 的内容,但是 per-accountId 锁的实现让我感到困惑。任何帮助将不胜感激。

4

2 回答 2

2

对于具有相同 AccountId 的请求,您必须锁定同一对象,但对每个单独的 AccountId 使用不同的对象。下面是如何使用 Dictionary 来跟踪单个 AccountIds 的锁定对象的示例。

    Dictionary<int, Object> Locks = new Dictionary<int, object>();

    List<item> GetItems(int AccountID)
    {
        //Get different lock object for each AccountId
        Object LockForAccountId = GetLockObject(AccountID);

        //block subsequent threads until first thread fills the cache
        lock (LockForAccountId)
        {
            var x = getFromCache(AccountID);
            if (x == null)
            {
                x = getFromDatabase(AccountID);
                addToCache(AccountID, x);
            }
            return x;
        }
    }

    private Object GetLockObject(int AccountID)
    {
        Object LockForAccountId;

        //we must use global lock while accessing dictionary with locks to prevent multiple different lock objects to be created for the same AccountId
        lock (Locks)
        {
            if (!Locks.TryGetValue(AccountID, out LockForAccountId))
            {
                LockForAccountId = new Object();
                Locks[AccountID] = LockForAccountId;
            }
        }
        return LockForAccountId;
    }
于 2015-10-09T01:03:10.113 回答
0

你有没有想过用Lazy<T>这个?

试试这个代码:

private object _gate = new object();
List<item> GetItems(int AccountID)
{
    lock (_gate)
    {
        var x = getFromCache(AccountID);
        if (x == null)
        {
            x = new Lazy<List<item>>(() => getFromDatabase(AccountID));
            addToCache(AccountID, x);
        }
        return x.Value;
    }
}

您需要更改getFromCache&addToCache以具有以下签名:

Lazy<List<item>> getFromCache(int AccountID)
void addToCache(int AccountID, Lazy<List<item>> x)
于 2015-10-09T05:50:48.530 回答