Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当valueFactory有副作用时,ConcurrentDictionary.GetOrAdd_C#_Database_Multithreading_Concurrency_Concurrentdictionary - Fatal编程技术网

C# 当valueFactory有副作用时,ConcurrentDictionary.GetOrAdd

C# 当valueFactory有副作用时,ConcurrentDictionary.GetOrAdd,c#,database,multithreading,concurrency,concurrentdictionary,C#,Database,Multithreading,Concurrency,Concurrentdictionary,我试图通过为一些非常核心的函数引入缓存层来减轻数据库服务器的工作量,这些函数将值插入数据库中的表并检索id。这是在多线程环境中实现的 我的第一个方法是: public class Cache { private Dictionary<string, Int64> i; public void Init() { /* init i with values from DB */ } public Int64 Get(string value)

我试图通过为一些非常核心的函数引入缓存层来减轻数据库服务器的工作量,这些函数将值插入数据库中的表并检索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;
      }
 }

有更好的方法吗?这是线程安全的吗?

您试图做的是惰性地创建一个只需要创建一次的对象,然后在创建后由任意数量的线程访问<代码>惰性正是为以下目的而设计的:

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();
    }
}
公共类缓存
{
私用词典;
public void Init(){/*Init i,值来自DB*/}
公共Int64获取(字符串值)
{
返回i.GetOrAdd(值,新延迟(()=>
CreateDatabaseRecordAndGetId())
价值
}
私有长CreateDatabaseRecordAndGetId()
{
抛出新的NotImplementedException();
}
}

仅供参考,在Servy的示例中,您会得到一个为每次调用
GetOrAdd
创建的
Lazy
实例。现在,
Lazy
的魔力仍然存在,您只需要调用一次创建实例的
Func
。但是上面示例中的
Lazy
的额外实例化可能解释了您在尝试时看到的内存增加

如果创建一个“double”lambda,就不会得到Lazy的多个实例化

例如,将其粘贴到一个控制台应用程序中,并比较有无
x=>new Lazy…
的实现如下:

public static class LazyEvaluationTesting
{
    private static readonly ConcurrentDictionary<int, CustomLazy<CacheableItem>>
        cacheableItemCache = new ConcurrentDictionary<int, CustomLazy<CacheableItem>>();

    private static CacheableItem RetrieveCacheableItem(int itemId)
    {
        Console.WriteLine("--RETRIEVE called\t ItemId [{0}] ThreadId [{1}]", itemId, Thread.CurrentThread.ManagedThreadId);
        return new CacheableItem
        {
            ItemId = itemId
        };
    }

    private static void GetCacheableItem(int itemId)
    {
        Console.WriteLine("GET called\t ItemId [{0}] ThreadId [{1}]", itemId, Thread.CurrentThread.ManagedThreadId);

        CacheableItem cacheableItem = cacheableItemCache
            .GetOrAdd(itemId,
                x => new CustomLazy<CacheableItem>(
                    () => RetrieveCacheableItem(itemId)
                )
            ).Value;

        //CacheableItem cacheableItem2 = cacheableItemCache
        //  .GetOrAdd(itemId,
        //      new CustomLazy<CacheableItem>(
        //          () => RetrieveCacheableItem(itemId)
        //      )
        //  ).Value;
    }

    public static void TestLazyEvaluation()
    {
        int[] itemIds = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 };
        ParallelOptions options = new ParallelOptions
        {
            MaxDegreeOfParallelism = 75
        };

        Parallel.ForEach(itemIds, options, itemId =>
        {
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
        });
    }

    private class CustomLazy<T> : Lazy<T> where T : class
    {
        public CustomLazy(Func<T> valueFactory)
            : base(valueFactory)
        {
            Console.WriteLine("-Lazy Constructor called  ThreadId [{0}]", Thread.CurrentThread.ManagedThreadId);
        }
    }

    private class CacheableItem
    {
        public int ItemId { get; set; }
    }
}
公共静态类LazyEvaluationTesting
{
私有静态只读ConcurrentDictionary
cacheableItemCache=新的ConcurrentDictionary();
私有静态CacheableItem RetrieveCacheableItem(int-itemId)
{
WriteLine(“--RETRIEVE called\t ItemId[{0}]ThreadId[{1}]”,ItemId,Thread.CurrentThread.ManagedThreadId);
返回新的可缓存项
{
ItemId=ItemId
};
}
私有静态void GetCacheableItem(int-itemId)
{
WriteLine(“调用\t ItemId[{0}]ThreadId[{1}]”,ItemId,Thread.CurrentThread.ManagedThreadId);
CacheableItem CacheableItem=cacheableItemCache
.GetOrAdd(项目ID,
x=>newcustomlazy(
()=>RetrieveCacheableItem(itemId)
)
).价值;
//CacheableItem cacheableItem2=cacheableItemCache
//.GetOrAdd(项目ID,
//新习惯(
//()=>RetrieveCacheableItem(itemId)
//      )
//)。价值;
}
公共静态void TestLazyEvaluation()
{
int[]itemIds={1,2,3,4,5,1,2,3,4,5,1,2,3,4,5};
ParallelOptions=新的ParallelOptions
{
MaxDegreeOfParallelism=75
};
Parallel.ForEach(itemIds,options,itemId=>
{
GetCacheableItem(itemId);
GetCacheableItem(itemId);
GetCacheableItem(itemId);
GetCacheableItem(itemId);
GetCacheableItem(itemId);
});
}
私有类CustomLazy:Lazy,其中T:class
{
公共定制(Func valueFactory)
:基本值(工厂值)
{
WriteLine(“-Lazy构造函数名为ThreadId[{0}]”,Thread.CurrentThread.ManagedThreadId);
}
}
私有类可缓存项
{
公共int ItemId{get;set;}
}
}

来源:

我不会锁定您试图修改的资源。@DanielA.White为什么不?这是一个非常好的做法。@Servy您不知道.net framework是否会锁定该引用。@Servy@DanielA.White我希望
ConcurrentDictionary
的定义遵循您链接的文章中的建议,不会锁定自己,因为MS知道这样做是个坏主意。粗略检查一下它的定义,让我相信它不会像预期的那样锁定自己。谢谢你给我指出懒惰。难道不需要LazyThreadSafetyMode.ExecutionAndPublication吗?@KasperVidebæk这是默认行为。基本上,它希望所有的东西都需要完全同步,除非你明确告诉它不要。谢谢这种方法比我目前的方法更安全还是更快?@KasperVidebæk都是安全的。如果两个线程各自尝试添加一个新项,则它们不需要等待对方来运行它们的初始化方法,这样会更快。在本例中,每当一个线程初始化一个值时,其他线程都不能初始化任何其他值。它还更清晰、更容易使用,因为它使用的是更高级别的工具,这些工具准确地表示了您想要做的事情,因此没有错误或误解的余地。我的缓存从1.6GB的内存占用空间变为2.3GB的内存占用空间。我通过编辑创建了一个不需要存储惰性对象的版本。你同意这一点吗?你是对的,你没有得到Lazy的多个实例化,但是你可以得到CustomLazy的多个实例化。考虑到第二个Lazy被删除,并且只有一个Lazy实例的Value属性被访问,我看不出这有多好。。。我不知道你是怎么得出这个结论的。在我的示例中,CustomLazy继承自Lazy,只是在构造函数中输出一条消息,让您查看是否调用了它。从其他方面看,它就像懒惰一样。您是真的运行了我的示例代码,还是只是做了一个纸上谈兵的观察?因为当我运行它时,我只看到我的消息被输出一次。我还想猜你没有读到我链接的博客条目?对不起,我的便条不是很清楚,我把我想说的话弄糊涂了。您的版本更好,因为它并不总是创建一个懒散的消息传递给GetOrAdd。我只是想
public static class LazyEvaluationTesting
{
    private static readonly ConcurrentDictionary<int, CustomLazy<CacheableItem>>
        cacheableItemCache = new ConcurrentDictionary<int, CustomLazy<CacheableItem>>();

    private static CacheableItem RetrieveCacheableItem(int itemId)
    {
        Console.WriteLine("--RETRIEVE called\t ItemId [{0}] ThreadId [{1}]", itemId, Thread.CurrentThread.ManagedThreadId);
        return new CacheableItem
        {
            ItemId = itemId
        };
    }

    private static void GetCacheableItem(int itemId)
    {
        Console.WriteLine("GET called\t ItemId [{0}] ThreadId [{1}]", itemId, Thread.CurrentThread.ManagedThreadId);

        CacheableItem cacheableItem = cacheableItemCache
            .GetOrAdd(itemId,
                x => new CustomLazy<CacheableItem>(
                    () => RetrieveCacheableItem(itemId)
                )
            ).Value;

        //CacheableItem cacheableItem2 = cacheableItemCache
        //  .GetOrAdd(itemId,
        //      new CustomLazy<CacheableItem>(
        //          () => RetrieveCacheableItem(itemId)
        //      )
        //  ).Value;
    }

    public static void TestLazyEvaluation()
    {
        int[] itemIds = { 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 };
        ParallelOptions options = new ParallelOptions
        {
            MaxDegreeOfParallelism = 75
        };

        Parallel.ForEach(itemIds, options, itemId =>
        {
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
            GetCacheableItem(itemId);
        });
    }

    private class CustomLazy<T> : Lazy<T> where T : class
    {
        public CustomLazy(Func<T> valueFactory)
            : base(valueFactory)
        {
            Console.WriteLine("-Lazy Constructor called  ThreadId [{0}]", Thread.CurrentThread.ManagedThreadId);
        }
    }

    private class CacheableItem
    {
        public int ItemId { get; set; }
    }
}