我正在寻找在多线程Win服务中使用单例进行日志记录,并想知道我可能遇到的一些问题。我已经设置了get实例来处理与
的同步 private static volatile Logging _instance;
private static object _syncRoot = new object();
private Logging(){}
public static Logging Instance
{
get
{
if (_instance==null)
{
lock(_syncRoot)
{
if (_instance == null)
{
_instance = new Logging();
}
}
}
return _instance;
}
}
还有什么我可能需要担心的吗?
答案 0 :(得分:13)
答案 1 :(得分:12)
这比其他任何信息更具信息性。
您发布的内容是双重检查锁定算法 - 据我所知,您发布的将工作。 (从Java 1.5开始,它也适用于那里。)然而,它非常脆弱 - 如果你有任何错误,你可能会引入非常微妙的竞争条件。
我通常更喜欢在静态初始化程序中初始化单例:
public class Singleton
{
private static readonly Singleton instance = new Singleton();
public static Singleton Instance
{
get { return instance; }
}
private Singleton()
{
// Do stuff
}
}
(如果你想要一点额外的懒惰,可以添加一个静态构造函数。)
这种模式更容易正确,在大多数情况下也是如此。
我C# singleton implementation page上有更多细节(也由迈克尔联系)。
至于危险 - 我说最大的问题是你失去了可测试性。可能太不适合记录。
答案 2 :(得分:3)
Singleton有可能成为访问该类所包含的资源的瓶颈,并强制顺序访问可以并行使用的资源。
在这种情况下,这可能不是一件坏事,因为您不希望同一时刻有多个项目写入您的文件,即便如此,我认为您的实现不会产生这种结果。但这是值得注意的事情。
答案 3 :(得分:2)
您需要确保记录器中的每个方法都可以安全地并发运行,即没有正确锁定它们不会写入共享状态。
答案 4 :(得分:1)
您正在使用double-checked locking被视为反模式的内容。 Wikipedia具有针对不同语言进行延迟初始化和不进行初始化的模式。
创建单例实例后,您当然必须确保所有方法都是线程安全的。
答案 5 :(得分:1)
更好的建议是在单线程设置步骤中建立记录器,因此保证在您需要时存在。在Windows服务中,OnStart是一个很好的地方。
另一个选择是使用System.Threading.Interlocked.CompareExchange(T%,T,T):T方法切换出来。它不那么令人困惑,而且可以保证工作。
System.Threading.Interlocked.CompareExchange<Logging>(_instance, null, new Logging());
答案 6 :(得分:1)
如果使用双重检查锁定模式并希望它适用于所有内存模型,则需要对第一次检查null使用Thread.VolatileRead()进行争论。辩论的一个例子可以在http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/b1932d46-877f-41f1-bb9d-b4992f29cedc/阅读。
那就是说,我通常使用Jon Skeet的解决方案。
答案 7 :(得分:0)
我认为如果Logging实例方法是线程安全的,则无需担心。