簡體   English   中英

C#靜態類私有字段是否安全?

[英]Are C# static class private fields thread safe?

我有一個從多個線程訪問的C#靜態類。 兩個問題:

  1. 在聲明初始化字段時,我的私有靜態字段是否安全?
  2. 從靜態構造函數中創建私有靜態字段時,我應該鎖定嗎?

使用來自不同線程的靜態類:

class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 100; i++)
            {
                Task.Run(() =>
                {
                    string name = MyStaticClass.GetValue(9555);
                    //...
                });
            }
        }
}

靜態類的選項1:

public static class MyStaticClass
    {
        private static MyClass _myClass = new MyClass();

        public static string GetValue(int key)
        {
            return _myClass.GetValue(key);
        }
    }

靜態類的選項2:

public static class MyStaticClass
    {
        private static MyClass _myClass;
        private static object _lockObj = new object();

        static MyStaticClass()
        {
            InitMyClass();
        }

        private static void InitMyClass()
        {
            if (_myClass == null)
            {
                lock(_lockObj)
                {
                    if (_myClass == null)
                    {
                        _myClass = new MyClass();
                    }
                }
            }
        }

        public static string GetValue(int key)
        {
            return _myClass.GetValue(key);
        }
    }

從靜態類創建的實例類:

public class MyClass
    {
        private Dictionary<int, Guid> _valuesDict = new Dictionary<int, Guid>();

        public MyClass()
        {
            for (int i = 0; i < 10000; i++)
            {
                _valuesDict.Add(i, Guid.NewGuid());
            }
        }

        public string GetValue(int key)
        {
            if (_valuesDict.TryGetValue(key, out Guid value))
            {
                return value.ToString();
            }

            return string.Empty;
        }
    }

從靜態構造函數中初始化私有靜態字段時,我應該鎖定嗎?

我們不要在這里埋葬lede:

永遠不要鎖定靜態構造函數 靜態構造函數已被框架鎖定,因此它們只在一個線程上運行一次。

這是一個更普遍的好建議的特例: 永遠不要對靜態構造函數中的線程做任何想象 靜態構造函數被有效鎖定,並且任何訪問您的類型的代碼都可以對鎖進行爭論,這意味着您可以非常快速地陷入您不期望並且很難看到的死鎖。 我在這里舉個例子: https//ericlippert.com/2013/01/31/the-no-lock-deadlock/

如果您想要延遲初始化,請使用Lazy<T>構造; 它是由知道如何使其安全的專家撰寫的。

在聲明初始化字段時,我的私有靜態字段是否安全?

線程安全性是在從多個線程調用程序元素時保留程序不變量。 你沒有說過你的不變量,所以不可能說你的程序是否“安全”。

如果您擔心的不變量是靜態構造函數在執行第一個靜態方法之前運行,或者創建了第一個類型的實例,則C#保證這一點。 當然,如果你在靜態構造函數中編寫瘋狂的代碼,那么瘋狂的事情就會發生,所以再次嘗試保持靜態構造函數非常簡單。

默認情況下,靜態類的字段不是線程安全的,除非它僅用於讀取目的,否則應避免使用。 這里的下方也是“鎖定”,它將在多線程環境中創建序列化處理。

public static class MyStaticClass
{
    private static MyClass _myClass;
    private static object _lockObj;

    static MyStaticClass()
    {
           _myClass = new MyClass();
           _lockObj = new object();
    }

    public static string GetValue(int key)
    {
        return _myClass.GetValue(key);
    }
    public static void SetValue(int key)
    {
        lock(_lockObj)
        {           
             _myClass.SetValue(key);
        }
    }
}

你的第二個版本更可取。 您可以通過readonly字段將其鎖定一點:

public static class MyStaticClass
{
    private static readonly MyClass _myClass = new MyClass();

    public static string GetValue(int key)
    {
        return _myClass.GetValue(key);
    }
}

您的意圖似乎是_myClass最初設置為MyClass一個實例,並且從不設置為另一個。 readonly通過指定它只能在靜態構造函數中設置一次或通過如上所述初始化來完成它。 不僅另一個線程沒有設置它,但任何改變它的嘗試都將導致編譯器錯誤。

您可以省略readonly並且永遠不會再次設置_myClass ,但readonly _myClass傳達並強制執行您的意圖。

這里變得更加棘手:您 MyClass實例的引用是線程安全的。 您不必擔心各種線程是否會用不同的實例替換它(或將其設置為null),並且它將在任何線程嘗試與之交互之前進行實例化。

什么這就是讓MyClass線程安全的。 在不知道它做什么或如何與之互動的情況下,我無法說出需求或關注點是什么。

如果這是一個問題,一種方法是使用lock來防止不應發生的並發訪問,正如@ Mahi1722所證明的那樣。 我包含了答案中的代碼(不是抄襲,但如果答案發生了什么,那么這個答案就會引用一個不存在的答案。)

public static class MyStaticClass
{
    private static MyClass _myClass = new MyClass();
    private static object _lockObj = new object();

    public static string GetValue(int key)
    {
        return _myClass.GetValue(key);
    }

    public static void SetValue(int key)
    {
        lock(_lockObj)
        {           
             _myClass.SetValue(key);
        }
    }
}

與交互兩種方法_myClass使用鎖定_lockObject這意味着,而另一個線程正在執行的任一或者任何執行將發生阻塞。

這是一種有效的方法。 另一種方法是通過使用並發集合或在該類中實現此類鎖來實際使MyClass線程安全。 這樣,您就不必在使用MyClass實例的每個類中使用lock語句。 你可以使用它知道它在內部管理它。

兩者都是正確的,但不需要lock static constructor 所以,我會選擇第一個選項,它更短更清晰

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM