静态变量的线程安全初始化

时间:2010-06-04 02:54:17

标签: c# multithreading singleton

我一直在使用这种模式初始化我的类中的静态数据。它看起来对我来说是安全的,但我知道细微的线程问题是多么微妙。这是代码:

public class MyClass // bad code, do not use
{
    static string _myResource = "";
    static volatile bool _init = false;
    public MyClass()
    {
        if (_init == true) return;
        lock (_myResource)
        {
            if (_init == true) return;
            Thread.Sleep(3000); // some operation that takes a long time 
            _myResource = "Hello World";
            _init = true;
        }
    }
    public string MyResource { get { return _myResource; } }
}

这里有漏洞吗?也许有一种更简单的方法可以做到这一点。

更新:共识似乎是静态构造函数是要走的路。我使用静态构造函数提出了以下版本。

public class MyClass
{
    static MyClass() // a static constructor
    {
        Thread.Sleep(3000); // some operation that takes a long time 
        _myResource = "Hello World";
    }

    static string _myResource = null;

    public MyClass() { LocalString = "Act locally"; } // an instance constructor

    // use but don't modify
    public bool MyResourceReady { get { return _myResource != null; } }
    public string LocalString { get; set; }
}

我希望这更好。

5 个答案:

答案 0 :(得分:12)

您可以使用静态构造函数初始化静态变量,C#guarantee仅在每个AppDomain中调用一次。不确定你是否考虑过它们。

所以你可以读到这个:http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx(静态构造函数)

而且:Is the C# static constructor thread safe?

答案 1 :(得分:6)

lock()上执行_myResource并在lock()语句中更改它似乎是一个坏主意。 请考虑以下工作流程:

  1. 主题1调用MyClass()
  2. 在分配_init = true;后立即在行_myResource之前停止执行。
  3. 处理器切换到线程2.
  4. 主题2调用MyClass()。由于_init仍为false且参考_myResource已更改,因此会成功输入lock()语句块。
  5. _init仍为false,因此主题2重新分配_myResource
  6. 解决方法:创建静态object并锁定此对象而不是初始化资源:

    private static readonly object _resourceLock = new object();
    
    /*...*/
    
    lock(_resourceLock)
    {
        /*...*/
    }
    

答案 2 :(得分:3)

你的课不安全:

  1. 在锁定对象后,您可以更改锁定的对象。
  2. 您有一个属性可以在不锁定资源的情况下获取资源。
  3. 你锁定primitive type,这通常不是一个好习惯。
  4. 这应该适合你:

    public class MyClass
    {
        static readonly object _sync = new object();
        static string _myResource = "";
        static volatile bool _init = false;
    
        public MyClass()
        {
            if (_init == true) return;
            lock (_sync)
            {
                if (_init == true) return;
                Thread.Sleep(3000); // some operation that takes a long time 
                _myResource = "Hello World";
                _init = true;
            }
        }
    
        public string MyResource 
        { 
            get 
            { 
                MyClass ret; // Correct
                lock(_sync)
                {
                    ret = _myResource;
                }
                return ret;
            } 
        }
    }
    

    <强>更新
    正确,不应该直接返回静态资源...我已经相应地更正了我的例子。

答案 3 :(得分:1)

根据您的使用情况(即如果线程不需要使用此变量将信息相互传递),将成员变量标记为[ThreadStatic]可能是一种解决方案。
请参阅here

答案 4 :(得分:0)

static string _myResource = "";
...
public MyClass()
{
    ...
    lock (_myResource)
    {
    }
}

由于string interning,您不应该锁定字符串文字。如果锁定字符串文字并且多个类使用该字符串文字,那么您可能正在共享该锁。这可能会导致意外行为。