C# 自动存储事件和单例问题

C# 自动存储事件和单例问题,c#,singleton,autoresetevent,C#,Singleton,Autoresetevent,有人能告诉我下面的代码有什么问题吗?理想情况下,它应该首先启动一个线程,然后等待set事件。相反,它不会启动线程,只是停留在WaitOne()上 我很想知道线发生了什么,为什么 class Program { static void Main(string[] args) { Testing t = Testing.Instance; Console.Read(); } } class Testing { private

有人能告诉我下面的代码有什么问题吗?理想情况下,它应该首先启动一个线程,然后等待set事件。相反,它不会启动线程,只是停留在WaitOne()上

我很想知道线发生了什么,为什么

class Program
{   
    static void Main(string[] args)
    {
        Testing t = Testing.Instance;
        Console.Read();
    }
}


class Testing
{
    private static AutoResetEvent evt = new AutoResetEvent(false);
    public static Testing Instance = new Testing();

    private Testing()
    {
        Create();
        evt.WaitOne();
        Console.WriteLine("out");
    }

    private void Create()
    {
        Console.WriteLine("Starting thread");
        new Thread(Print).Start();
    }

    private void Print()
    {
        Console.WriteLine("started");
        evt.Set();
    }
}
编辑: 到目前为止,@BrokenGlass提供的描述是有意义的。但是将代码更改为以下代码允许另一个线程可以访问实例方法,而无需完成构造函数。(由@NicoSchertler建议)


我怀疑这种行为的根本原因是,在构造函数完成执行之前,派生线程无法访问
Print
方法,但构造函数从未完成执行,因为它正在等待仅从
Print
方法触发的信号


evt.WaitOne()
替换为一个长
线程。Sleep()
调用确认了相同的行为-构造函数必须在对象的任何实例方法从另一个线程执行之前完成运行。

我猜静态字段初始化的相对时间会有问题。尝试在
测试的构造函数中初始化
evt

private static AutoResetEvent evt;
public static Testing Instance = new Testing();

private Testing()
{
    evt = new AutoResetEvent(false);
    Create();
    evt.WaitOne();
    Console.WriteLine("out");
}

我应该注意到这只是一个猜测——我本以为这段代码可以很好地工作。

问题是第二个线程创建得太早了。我不知道为什么,但在主程序启动之前启动时,它将不会执行

您应该在其原始版本中使用singleton模式。这会奏效的

private static Testing _Instance;

public static Testing Instance
{
    get
    {
        if (_Instance == null)
            _Instance = new Testing();
        return _Instance;
    }
}

此外,不应使evt变量为静态变量。在大多数情况下,实例变量应该是singleton类的唯一静态成员。

您还没有编写“singleton”。我将继续获得
测试的新实例
实例的每次调用
。这不是一个单例。@IAbstract:
实例
是一个单例singleton@BrokenGlass,果然-实现静态实例只初始化了一次,但是这里的单例实现很差,IMO.@IAbstract我同意,但这只是一个示例,用于关注其他一些问题,而不是理想的单例模式。能否在
evt.Set()行上设置调试器断点并查看是否调用过它?如何调用它?不会调用它。@IAbstract在
Create
中,一个线程开始调用
Print
新线程(Print).Start()您的单例实现不是线程安全的-可能需要在此处查看更安全的方法:嗯。。这是有道理的。但是有人知道为什么修改后的singleton有效吗(见下面我的答案)?在调用evt.Set()之前,这里的构造函数也没有完成。@NicoSchertler:好问题,我不知道either@BrokenGlass我试过睡眠,你的理论是有道理的。但在那之后,我看到了尼科舍勒,我又回到了原点。我也不知道尼科的作品为什么会成功。我认为这可能与BeforeFieldInit有关,但我尝试了2.0和4.0.NET版本,并得到了相同的结果。乔恩·斯基特在哪里?啊!我确实知道它为什么有效。它之所以有效,是因为cctor不包含
newtesting()
。如果(\u Instance==null)
,则第一个成员访问发生在
处。在该行之后,cctor被完全执行。
private static Testing _Instance;

public static Testing Instance
{
    get
    {
        if (_Instance == null)
            _Instance = new Testing();
        return _Instance;
    }
}