在Implementing the Singleton Pattern in C#惊人的文章中,使用我所判断的最好的世界,我一直在成功使用以下类来将用户定义的数据保存在内存中(对于极少数修改过的数据): / p>
public class Params
{
static readonly Params Instance = new Params();
Params()
{
}
public static Params InMemory
{
get
{
return Instance;
}
}
private IEnumerable<Localization> _localizations;
public IEnumerable<Localization> Localizations
{
get
{
return _localizations ?? (_localizations = new Repository<Localization>().Get());
}
}
public int ChunkSize
{
get
{
// Loc uses the Localizations impl
LC.Loc("params.chunksize").To<int>();
}
}
public void RebuildLocalizations()
{
_localizations = null;
}
// other similar values coming from the DB and staying in-memory,
// and their refresh methods
}
我的用法看起来像这样:
var allLocs = Params.InMemory.Localizations; //etc
每当我更新数据库时,都会调用RefreshLocalizations,因此只重建了部分内存存储。我有一个单个生产环境,大约10个似乎在调用RefreshLocalizations时行为不端,根本没有刷新,但这似乎也是间歇性的,非常奇怪的是。
我当前的怀疑是针对单身人士,我认为这项工作非常出色,并且所有单元测试证明单例机制,刷新机制和RAM性能都按预期工作。
那就是说,我很清楚这些可能性:
有什么建议吗?
.NET 3.5 所以没有多少并行果汁,现在还没准备好使用Reactive Extensions
Edit1:根据建议,getter看起来像是:
public IEnumerable<Localization> Localizations
{
get
{
lock(_localizations) {
return _localizations ?? (_localizations = new Repository<Localization>().Get());
}
}
}
答案 0 :(得分:2)
如果您的属性不是线程安全的,那么创建线程安全单例是没有意义的。
您应该锁定_localization
字段的赋值,或者在单例的构造函数中实例化(首选)。任何适用于单例实例化的建议都适用于这个惰性实例化的属性。
同样的事情进一步适用于Localization
的所有属性(及其属性)。如果这是一个Singleton,这意味着任何线程都可以随时访问它,而只是锁定getter将再次无效。
例如,考虑这种情况:
Thread 1 Thread 2 // both threads access the singleton, but you are "safe" because you locked 1. var loc1 = Params.Localizations; var loc2 = Params.Localizations; // do stuff // thread 2 calls the same property... 2. var value = loc1.ChunkSize; var chunk = LC.Loc("params.chunksize"); // invalidate // ...there is a slight pause here... 3. loc1.RebuildLocalizations(); // ...and gets the wrong value 4. var value = chunk.To();
如果您只是阅读这些值,那么它可能并不重要,但您可以看到如何轻松地使用这种方法遇到麻烦。
请记住,使用线程,您永远不会知道不同的线程是否会在两条指令之间执行某些操作。 只有简单的32位分配是原子的,没有别的。
这意味着,在这一行:
return LC.Loc("params.chunksize").To<int>();
就线程而言,等同于:
var loc = LC.Loc("params.chunksize");
Thread.Sleep(1); // anything can happen here :-(
return loc.To<int>();
任何主题都可以在Loc
和To
之间跳转。
答案 1 :(得分:2)
要扩展我的评论,以下是如何使Localizations
属性线程安全:
public class Params
{
private object _lock = new object();
private IEnumerable<Localization> _localizations;
public IEnumerable<Localization> Localizations
{
get
{
lock (_lock) {
if ( _localizations == null ) {
_localizations = new Repository<Localization>().Get();
}
return _localizations;
}
}
}
public void RebuildLocalizations()
{
lock(_lock) {
_localizations = null;
}
}
// other similar values coming from the DB and staying in-memory,
// and their refresh methods
}