0

我遵循了实现单例类的代码(双重检查锁定)

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());

这是一些编译器优化的问题吗?请帮忙

4

2 回答 2

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 ]

内存模型允许对非易失性读\写进行重新排序,只要从单个线程的角度看不到这种变化。

即使与volatile. 关键字告诉编译器必须在读取字段后volatile写入字段。然而,没有什么能阻止它在首先读取 的值之前初始化新对象。_instance_instancePlugin_instance

除此之外,您说您还面临另一个问题:

实例在正确构造之前返回

然后你需要等待初始化完成,而不仅仅是检查它是否已经启动。显然,在类 Plugin 的构造函数结束之前已经设置了该字段_instance,如果是这种情况,则意味着您需要等到它完成。此外,如果那里有一些异步调用,您可能需要添加“就绪”属性或其他方式来等待[允许对象处于无效状态将是您的错]。

*:这通常可以通过引入一个时间变量来解决,您可以在其中设置新实例并将该变量分配给您的字段。该技术还允许通过添加内存屏障使该字段成为非易失性......然而,它增加了让您的构造函数运行不止一次的风险。所以,我跳过了所有这些。

要解决这两个问题,您可以使用 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;
        }
    }
}

即使......只需使用静态构造函数。

于 2012-10-17T07:04:21.707 回答
0

好的,这是听起来很幼稚的实际问题。带有上述代码的 dll 被加载到具有无效 exe.config 的主应用程序中。而且由于我的 dll 有单独的 dll.config(这是有效的),因此应用程序在通过调试器运行时工作正常,但是在部署环境中运行时(没有附加调试器),它遇到了无效的配置文件异常。

我已将主 exe.config 设置为有效的配置文件,它现在可以工作了。

所以基本上,解决方案就像检查构建过程中是否存在异常一样天真。

于 2012-10-22T11:26:38.260 回答