实现搜索结果的线程安全缓存

时间:2015-09-03 17:39:04

标签: c# asp.net search thread-safety

搜索结果缓存的工作原理

当用户输入query搜索:

  • 查询被拆分为令牌数组
  • 为此令牌数组创建一个唯一的哈希值(按字母顺序排列令牌,然后是MD5)。这是搜索的唯一标识。
  • 根据哈希
  • 检查缓存的结果
  • 如果缓存不存在,请使用哈希
  • 将结果保存到缓存中

问题我试图解决

如果用户执行的搜索时间为10秒,并且他们不耐烦地刷新页面,我们就不希望它再次启动查询。这应该被锁定。

但是,如果运行昂贵的查询,我们并不想锁定执行较便宜搜索的其他用户。

要解决这个问题,我需要多个锁。

实施

这就是我目前实施的方式:

private static readonly object MasterManualSearchLock = new object();
private static readonly Dictionary<string, object> ManualSearchLocks = new Dictionary<string, object>();

/// <summary>
/// Search the manual
/// </summary>
public static SearchResponse DoSearch(string query, Manual forManual)
{
    var tokens = Search.Functions.TokeniseSearchQuery(query);
    var tokenHash = Search.Functions.GetUniqueHashOfTokens(tokens);
    var cacheIndex = Settings.CachePrefix + "SavedManualSearch_" + tokenHash;
    var context = HttpContext.Current;

    if (context.Cache[cacheIndex] == null)
    {
        // Create lock if it doesn't exist
        if (!ManualSearchLocks.ContainsKey(tokenHash))
        {
            lock (MasterManualSearchLock)
            {
                if (!ManualSearchLocks.ContainsKey(tokenHash))
                {
                    ManualSearchLocks.Add(tokenHash, new object());
                }
            }

        }

        lock (ManualSearchLocks[tokenHash])
        {
            if (context.Cache[cacheIndex] == null)
            {
                var searchResponse = new SearchResponse(tokens, forManual, query);
                context.Cache.Add(cacheIndex, searchResponse, null, DateTime.Now.AddMinutes(Settings.Search.SearchResultsAbsoluteTimeoutMins), Cache.NoSlidingExpiration, CacheItemPriority.BelowNormal, null);
            }
            ManualSearchLocks.Remove(tokenHash);
        }

    }
    return (SearchResponse)context.Cache[cacheIndex];
} 

问题

  • 这是一个合理的实施吗?
  • 这个线程安全吗?
  • 是否包括锁定锁内的锁定?

1 个答案:

答案 0 :(得分:0)

您同时使用ManualSearchLocks是不安全的,ConcurrentDictionary是一个很好的替代品。不,只是从字典中读取是不安全的,因为没有记录是安全的。

我将Lazy<T>放入缓存中。可能会生成多个这样的惰性实例,但只有一个会实现。想要访问特定密钥的所有线程都将调用Lazy.Value并自动进行同步。一旦实际&#34;搜索&#34;将被执行。

根据您访问缓存的方式,可能存在一个小的竞争条件,允许执行多个惰性实例。在你的情况下,这可能不是什么大问题。