在实际开发过程中,Dictionary是一种经常被用来做缓存的数据结构,既然是用来做缓存,那么不可避免的就要涉及到多线程的问题,如何来合理的使用,这是一个问题,这个问题与实际业务场景有很大关系,通常来说,会有这么几种场景
1, 一次性写入,不再改变
2, 存在写入与删除
对于缓存来说,永远是读操作占了大部分,因此,必须要确保对于读来说,不会影响并发,这是首要的原则,对于那种在读的时候需要lock的必须否定,对于第一种场景,没什么可说的,直接使用Dictionary就行,Dictionary在内容不变的情况下,对于读操作是可以并发的,并且这种情况下读性能是最好的。
对于第二种场景来说,又要做区分,需要根据缓存列表的预估长度以及写操作频繁度来选择,
如果预估长度不大或者写操作不频繁,建议通过“不变列表”模式来实现,具体来说,在写操作的代码中,这样写
1 //_lock为一个静态object,用来做lock操作 2 //_dict为静态缓存的Dictionary对象 3 lock(_lock) 4 { 5 //通过ToDictionary浅拷贝出一个新的Dictionary对象 6 //这样可以保证原先的_dict对于读操作“不变”,使读操作不收任何并发影响 7 var copyDict =_dict.ToDictionary((kv) => kv.Key,(kv)=>kv.Value); 8 //这里做新增删除的动作 9 copyDict["key"] = "value"; 10 //修改完成后重新为静态缓存赋值 11 _dict = copyDict; 12 }
这就是所谓的“不变列表”模式,从这里也可以看出,如果列表长度很大的话,其实每次执行浅拷贝的动作都会有不少的耗时,这个耗时是随着列表的长度线性增长的,这就是为什么这种模式适用于“预估长度不大或者写操作不频繁”这种场景。
那如果预估长度很大或者写操作很频繁怎么办呢,这个使用就要用到框架中的并行字典了,也就是ConcurrentDictionary,这个是线程安全的字典类,因此可以像单线程操作一样去使用,它的内部已经做了多线程的处理了,唯一的缺点是,在读的时候,由于内部依然会有锁的动作,是不如“不变列表”模式的性能高的。
以上就是使用Dictionary做缓存时要考虑的问题