我遵循一段实现单例类(Double-Check Locking)的代码
public sealed class Plugin
{
#region Private Fields
private static volatile Plugin _instance;
private static object syncRoot = new Object();
private Dictionary<int, string> myMap;
#endregion
private Plugin()
{
myMap = MapInit(GetMainModuleName());
}
static Plugin()
{ }
public static Plugin Instance
{
get
{
if (_instance == null)
{
lock (syncRoot)
{
if (_instance == null)
_instance = new Plugin();
}
}
return _instance;
}
}
}
单例实例在调试模式下正确构建,一切似乎都正常工作。但是在发布模式下,实例在正确构造之前返回,即myMap未初始化。
另外需要注意的是,以下代码需要大约10-15秒才能在调试模式下完全执行
myMap = MapInit(GetMainModuleName());
这是一些编译器优化的问题吗?请帮忙
答案 0 :(得分:1)
你不需要Singleton,实际上你不做Singleton。为什么这些天人们在做单身人士?
看,这很简单:
public sealed class Plugin
{
private static readonly Plugin _instance;
private /*readonly?*/ Dictionary<int, string> myMap;
private Plugin()
{
myMap = MapInit(GetMainModuleName());
}
static Plugin()
{
_instance = new Plugin();
}
public static Plugin Instance
{
get
{
return _instance;
}
}
}
静态构造函数保证每个应用程序域只运行一次,这是C#语言规范的一部分。
要解决您的问题,双重检查模式存在问题,因为您已经证明当机器在硬件中有多个线程时,它不适用于编译器优化。原因是......
[来自http://blogs.msdn.com/b/brada/archive/2004/05/12/130935.aspx]
内存模型允许重新排序非易失性读/写 只要从a的角度看不能发现这种变化 单线程。
即使使用volatile
。 volatile
关键字告诉编译器必须在读取字段_instance
后写入字段_instance
。但是,在首先读取Plugin
的值之前,没有什么可以阻止它初始化新的_instance
对象。
除此之外,你说你正面临另一个问题:
在构造正确之前返回实例
然后你需要等待初始化完成,而不仅仅是检查它是否已经启动。显然字段_instance
已经在类Plugin的构造函数结束之前设置了,如果是这种情况,则意味着你需要等到它完成。此外,如果有一些异步调用,你可能需要添加一个“就绪”属性或其他一些等待方式[允许一个对象处于无效状态是你的错误]。
*:这通常是通过引入时间变量来解决的,您可以在其中设置新实例以及将该变量分配给您的字段。该技术还允许通过添加内存屏障使字段非易失性......然而,它增加了使构造函数运行多次的风险。所以,我已经跳过了这一切。
要解决这两个问题,你可以使用Interlocked和ManualResetEvent的这种组合[不知道构造函数的内部我怀疑我可以做更多]:
public sealed class Plugin
{
private static readonly Plugin _instance;
private static int _initializing;
private static ManualReserEvent _done;
private Dictionary<int, string> myMap;
private Plugin()
{
myMap = MapInit(GetMainModuleName());
}
static Plugin()
{
_done = new ManualResetEvent(false);
}
public static Plugin Instance
{
get
{
if (Interlocked.CompareExchance(ref _initializing, 1, 0) == 0)
{
_instance = new Plugin();
_done.Set();
}
else
{
_done.WaitOne();
}
return _instance;
}
}
}
即使......只使用静态构造函数。
答案 1 :(得分:0)
好的,这是可能听起来很幼稚的实际问题。带有上述代码的dll被加载到主应用程序中,该应用程序的exe.config无效。由于我的dll分离了dll.config(这是有效的),应用程序在运行调试器时工作正常,但是当在部署环境中运行(没有附带调试器)时,它遇到了无效的配置文件异常。
我已将主exe.config作为有效的配置文件,现在可以正常工作。
所以基本上,解决方案与检查构造过程中是否存在异常一样天真。