当valueFactory有副作用时,ConcurrentDictionary.GetOrAdd

Kas*_*bæk 3 c# database concurrency multithreading concurrentdictionary

我试图通过为一些非常核心的函数引入缓存层来从我的数据库服务器卸载工作,这些函数将值插入数据库中的表并检索id.这是一个多线程环境.

我的第一个方法是:

public class Cache {
      private Dictionary<string, Int64> i;

      public void Init() { /* init i with values from DB */ }

      public Int64 Get(string value)
         lock(i) {
            Int64 id;
            if (cache.i.TryGetValue(value, out id))
                return id;

            id = /* Insert to DB and retrieve ID */
            cache.i[value] = id;
            return id;
      }
 }
Run Code Online (Sandbox Code Playgroud)

这有帮助.然而,线程仍然相互等待很多.我想减少这个等待时间.我的第一个想法是使用ConcurrentDictionary.GetOrAdd(key, valueFactory).这不起作用,因为可以多次调用valueFactory.

我已经结束了这种方法:

public class Cache
{
    private ConcurrentDictionary<string, Int64> i;

    public void Init() { /* init i with values from DB */ }

    public Int64 Get(string value)
    {
        Int64 id;
        if (i.TryGetValue(value, out id))
            return id;

        lock (i)
        {
            if (i.TryGetValue(value, out id))
                return id;

            id = /* Insert to DB and retrieve ID */
            i.TryAdd(value, id);
            return id;
        }
    }
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法呢?这是否是线程安全的?

Ser*_*rvy 11

你要做的是懒洋洋地创建一个需要创建的对象不超过一次,然后创建后由任意数量的线程访问. Lazy专为此而设计:

public class Cache
{
    private ConcurrentDictionary<string, Lazy<long>> i;

    public void Init() { /* init i with values from DB */ }

    public Int64 Get(string value)
    {
        return i.GetOrAdd(value, new Lazy<long>(() =>
            CreateDatabaseRecordAndGetId()))
            .Value;
    }

    private long CreateDatabaseRecordAndGetId()
    {
        throw new NotImplementedException();
    }
}
Run Code Online (Sandbox Code Playgroud)