2008-08-10 15 views
213

दूसरे शब्दों में, इस सिंगलटन कार्यान्वयन धागा सुरक्षित है: "। एक प्रकार प्रारंभकर्ता, किसी भी प्रकार के लिए ठीक एक बार चलाने जाएगा, जब तक स्पष्ट रूप से उपयोगकर्ता कोड से बुलाया"सी # स्थिर कन्स्ट्रक्टर थ्रेड सुरक्षित है?

public class Singleton 
{ 
    private static Singleton instance; 

    private Singleton() { } 

    static Singleton() 
    { 
     instance = new Singleton(); 
    } 

    public static Singleton Instance 
    { 
     get { return instance; } 
    } 
} 
+0

यह थ्रेड-सुरक्षित है। मान लीजिए कि कई धागे एक बार में संपत्ति 'इंस्टेंस' प्राप्त करना चाहते हैं। धागे में से एक को पहले टाइप प्रारंभकर्ता (जिसे स्थिर कन्स्ट्रक्टर भी कहा जाता है) चलाने के लिए कहा जाएगा। इस बीच 'इंस्टेंस' प्रॉपर्टी को पढ़ने की इच्छा रखने वाले सभी अन्य थ्रेड, टाइप लॉन्च होने तक *** लॉक हो जाएंगे ***। फ़ील्ड प्रारंभकर्ता के निष्कर्ष निकालने के बाद ही, थ्रेड को 'इंस्टेंस' मान प्राप्त करने की अनुमति होगी। तो कोई भी 'इंस्टेंस' 'शून्य 'नहीं देख सकता है। –

उत्तर

162

स्थिर रचनाकारों को प्रति वर्ग डोमेन के केवल एक बार चलाने के लिए गारंटी दी जाती है, कक्षा के किसी भी उदाहरण के निर्माण से पहले या किसी स्थिर सदस्य का उपयोग किया जाता है। http://msdn.microsoft.com/en-us/library/aa645612.aspx

दिखाया गया कार्यान्वयन प्रारंभिक निर्माण के लिए थ्रेड सुरक्षित है, यानी सिंगलटन ऑब्जेक्ट के निर्माण के लिए कोई लॉकिंग या नल परीक्षण आवश्यक नहीं है। हालांकि, इसका मतलब यह नहीं है कि इंस्टेंस का कोई भी उपयोग सिंक्रनाइज़ किया जाएगा। ऐसे कई तरीके हैं जिनसे यह किया जा सकता है; मैंने नीचे एक दिखाया है।

public class Singleton 
{ 
    private static Singleton instance; 
    // Added a static mutex for synchronising use of instance. 
    private static System.Threading.Mutex mutex; 
    private Singleton() { } 
    static Singleton() 
    { 
     instance = new Singleton(); 
     mutex = new System.Threading.Mutex(); 
    } 

    public static Singleton Acquire() 
    { 
     mutex.WaitOne(); 
     return instance; 
    } 

    // Each call to Acquire() requires a call to Release() 
    public static void Release() 
    { 
     mutex.ReleaseMutex(); 
    } 
} 
+47

ध्यान दें कि यदि आपकी सिंगलटन ऑब्जेक्ट अपरिवर्तनीय है, तो म्यूटेक्स या किसी सिंक्रनाइज़ेशन तंत्र का उपयोग करना एक ओवरकिल है और इसका उपयोग नहीं किया जाना चाहिए। इसके अलावा, मुझे उपरोक्त नमूना कार्यान्वयन अत्यंत भंगुर लगता है :-)। सिंगलटन.एक्वायर() का उपयोग कर सभी कोड सिंगलटन को कॉल करने की उम्मीद है। कृपया() जब सिंगलटन इंस्टेंस का उपयोग करके किया जाता है। ऐसा करने में विफल (उदा। समय-समय पर लौटना, अपवाद के माध्यम से गुंजाइश छोड़ना, रिलीज को कॉल करना भूलना), अगली बार जब सिंगलटन को एक अलग थ्रेड से एक्सेस किया जाता है तो यह सिंगलटन में डेडलॉक होगा। एक्वायर()। –

+2

सहमत हुए, हालांकि मैं आगे जाऊंगा। यदि आपका सिंगलटन अपरिवर्तनीय है, तो सिंगलटन का उपयोग करना अधिक है। बस स्थिरांक परिभाषित करें। आखिरकार, एक सिंगलटन का उपयोग करने के लिए उचित रूप से डेवलपर्स को पता है कि वे क्या कर रहे हैं। इस कार्यान्वयन के रूप में भंगुर के रूप में, यह अभी भी प्रश्न में से एक से बेहतर है जहां उन त्रुटियों को स्पष्ट रूप से अप्रकाशित म्यूटेक्स के बजाय यादृच्छिक रूप से प्रकट होता है। – Zooba

+23

रिलीज() विधि की कठोरता को कम करने का एक तरीका है सिंक हैंडलर के रूप में IDisposable के साथ एक और वर्ग का उपयोग करना है। जब आप सिंगलटन प्राप्त करते हैं, तो आपको हैंडलर मिल जाता है और रिलीज को संभालने के लिए सिंगलटन को एक ब्लॉक में उपयोग करने के लिए कोड डाल सकता है। – CodexArcanum

2

Common Language Infrastructure specification गारंटी देता है कि (धारा 9.5.3.1।) तो जब तक आपके पास ढीले कॉलिंग सिंगलटन :: पर कुछ अजीब आईएल नहीं है। सीक्टर सीधे (संभावना नहीं) आपका स्थिर कन्स्ट्रक्टर सिंगलटन प्रकार का उपयोग करने से ठीक पहले एक बार चलाएगा, सिंगलटन का केवल एक उदाहरण बनाया जाएगा, और आपकी इंस्टेंस प्रॉपर्टी थ्रेड-सुरक्षित है।

ध्यान दें कि यदि सिंगलटन का कन्स्ट्रक्टर इंस्टेंस प्रॉपर्टी (अप्रत्यक्ष रूप से) तक पहुंचता है तो इंस्टेंस प्रॉपर्टी शून्य हो जाएगी। सबसे अच्छा आप यह कर सकते हैं कि यह कब होता है और एक अपवाद फेंकता है, यह जांचकर कि संपत्ति एक्सेसर में यह सबकुछ शून्य नहीं है। आपके स्थैतिक कन्स्ट्रक्टर को पूरा करने के बाद इंस्टेंस प्रॉपर्टी गैर-शून्य हो जाएगी।

Zoomba's answer बताते हैं कि आपको सिंगलटन को एकाधिक थ्रेड से एक्सेस करने के लिए सुरक्षित करना होगा, या सिंगलटन इंस्टेंस का उपयोग करके लॉकिंग तंत्र को लागू करना होगा।

22

एक स्थिर निर्माता का उपयोग करना वास्तव में threadsafe है। स्थिर कन्स्ट्रक्टर को केवल एक बार निष्पादित करने की गारंटी है।

सी # भाषा विनिर्देश http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx से:

एक वर्ग के लिए स्थिर निर्माता किसी दिए गए आवेदन डोमेन में अधिकतम एक बार निष्पादित करता है। एक स्थिर कन्स्ट्रक्टर का निष्पादन निम्न अनुप्रयोगों में से किसी एक अनुप्रयोग डोमेन के भीतर होता है:

  • कक्षा का एक उदाहरण बनाया गया है।
  • कक्षा के किसी भी स्थिर सदस्य का संदर्भ दिया जाता है।

तो हाँ, आप विश्वास कर सकते हैं कि आपके सिंगलटन सही ढंग से instantiated कर दिया जाएगा।

Zooba (भी मेरे सामने और 15 सेकंड!) एक उत्कृष्ट मुद्दा बनाया है कि स्थिर निर्माता सिंगलटन के धागे की सुरक्षित साझा पहुंच की गारंटी नहीं होंगे। इसे किसी अन्य तरीके से संभालने की आवश्यकता होगी।

3

स्टेटिक कंस्ट्रक्टर्स अनुप्रयोग डोमेन प्रति केवल एक बार सक्रिय करने के लिए तो अपने दृष्टिकोण ठीक होना चाहिए की गारंटी है। हालांकि, यह कार्यात्मक अधिक संक्षिप्त, इनलाइन संस्करण से अलग नहीं है:

private static readonly Singleton instance = new Singleton(); 

थ्रेड सुरक्षा एक मुद्दे के अधिक है जब आप lazily बातें आरंभ कर रहे हैं।

+3

एंड्रयू, यह पूरी तरह से समकक्ष नहीं है। हमारे द्वारा नहीं एक स्थिर कन्स्ट्रक्टर में, प्रारंभिककर्ता को निष्पादित करने के बारे में कुछ गारंटी खो दी जाएगी। कृपया इन लिंक को गहराई से स्पष्टीकरण के लिए देखें: * *

+0

डेरेक, मैं * prefieldinit * "अनुकूलन" से परिचित हूं, लेकिन, व्यक्तिगत रूप से, मैं इसके बारे में कभी चिंता नहीं करता हूं। –

+0

@ डेरेकपार्क की टिप्पणी के लिए कामकाजी लिंक: http://csharpindepth.com/Articles/General/Beforefieldinit.aspx। यह लिंक पुराना प्रतीत होता है: http://www.ondotnet.com/pub/a/dotnet/2003/07/07/staticxtor.html – phoog

74

इन उत्तरों के सभी एक ही सामान्य जवाब दे रहे हैं, वहीं एक चेतावनी है।

याद रखें कि एक सामान्य वर्ग के सभी संभावित व्युत्पन्न व्यक्तिगत प्रकार के रूप में संकलित किए जाते हैं। तो जेनेरिक प्रकारों के लिए स्थिर रचनाकारों को लागू करते समय सावधानी बरतें।

class MyObject<T> 
{ 
    static MyObject() 
    { 
     //this code will get executed for each T. 
    } 
} 

संपादित करें:

static void Main(string[] args) 
{ 
    var obj = new Foo<object>(); 
    var obj2 = new Foo<string>(); 
} 

public class Foo<T> 
{ 
    static Foo() 
    { 
     System.Diagnostics.Debug.WriteLine(String.Format("Hit {0}", typeof(T).ToString()));   
    } 
} 

कंसोल में:

यहाँ प्रदर्शन है

Hit System.Object 
Hit System.String 
+0

टाइपऑफ (MyObject )! = टाइपऑफ (MyObject ); –

+3

मुझे लगता है कि वह बिंदु है जिसे मैं बनाने की कोशिश कर रहा हूं। जेनेरिक प्रकारों को अलग-अलग प्रकार के रूप में संकलित किया जाता है, जिनके आधार पर जेनेरिक पैरामीटर का उपयोग किया जाता है, इसलिए स्थैतिक कन्स्ट्रक्टर कई बार कॉल किया जा सकता है। –

+0

यह सही है जब टी मान प्रकार का है, संदर्भ प्रकार टी के लिए केवल एक सामान्य प्रकार उत्पन्न होगा – sll

2

बस पंडिताऊ होने के लिए, लेकिन वहाँ एक स्थिर जैसी कोई चीज नहीं है कन्स्ट्रक्टर, बल्कि स्थिर प्रकार के शुरुआती, here's a small चक्रीय स्थैतिक कन्स्ट्रक्टर डी का डेमो लटकन जो इस बिंदु को दर्शाता है।

+0

माइक्रोसॉफ्ट असहमत प्रतीत होता है। https://msdn.microsoft.com/en-us/library/k9x6w0hc.aspx – Westy92

7

यहाँ C# सिंगलटन पर MSDN ऊपर पेज से Cliffnotes संस्करण है:

निम्नलिखित पैटर्न का उपयोग करें, हमेशा की तरह, आप नहीं गलत हो सकता है:

public sealed class Singleton 
{ 
    private static readonly Singleton instance = new Singleton(); 

    private Singleton(){} 

    public static Singleton Instance 
    { 
     get 
     { 
     return instance; 
     } 
    } 
} 

स्पष्ट सिंगलटन सुविधाओं के अलावा, यह देता है आप मुक्त करने के लिए इन दो चीजों (संबंध में ग में सिंगलटन को ++):

  1. आलसी निर्माण (या कोई निर्माण करता है, तो यह कभी नहीं कहा जाता था)
  2. सिंक्रनाइज़ेशन
+2

आलसी अगर कक्षा में कोई अन्य असंबंधित स्थिरता नहीं है (जैसे कॉन्स)। अन्यथा किसी भी स्थिर विधि या संपत्ति का उपयोग परिणाम उदाहरण निर्माण होगा। तो मैं इसे आलसी नहीं कहूंगा। – Schultz9999

+1

@ShmilTheCat: सी ++ को इसके साथ क्या करना है? – stakx

0

हालांकि अन्य उत्तर अधिकतर सही हैं, फिर भी स्थिर रचनाकारों के साथ एक और चेतावनी है।

अनुभाग II.10.5.3.3 दौड़ के अनुसार और ECMA-335 Common Language Infrastructure

प्रकार प्रारंभ की गतिरोध अकेले एक गतिरोध पैदा नहीं होगा जब तक कि कुछ कोड एक प्रकार प्रारंभकर्ता (प्रत्यक्ष या परोक्ष) से ​​कहा जाता है स्पष्ट रूप से अवरुद्ध संचालन का आह्वान करता है।

एक गतिरोध में निम्न कोड परिणाम

using System.Threading; 
class MyClass 
{ 
    static void Main() { /* Won’t run... the static constructor deadlocks */ } 

    static MyClass() 
    { 
     Thread thread = new Thread(arg => { }); 
     thread.Start(); 
     thread.Join(); 
    } 
} 

मूल लेखक इगोर Ostrovsky है, अपने पद here देखते हैं।

1

स्थिर निर्माता से पहले किसी भी धागा वर्ग का उपयोग करने की अनुमति दी है चल रहा है खत्म हो जाएगा

private class InitializerTest 
    { 
     static private int _x; 
     static public string Status() 
     { 
      return "_x = " + _x; 
     } 
     static InitializerTest() 
     { 
      System.Diagnostics.Debug.WriteLine("InitializerTest() starting."); 
      _x = 1; 
      Thread.Sleep(3000); 
      _x = 2; 
      System.Diagnostics.Debug.WriteLine("InitializerTest() finished."); 
     } 
    } 

    private void ClassInitializerInThread() 
    { 
     System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() starting."); 
     string status = InitializerTest.Status(); 
     System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() status = " + status); 
    } 

    private void classInitializerButton_Click(object sender, EventArgs e) 
    { 
     new Thread(ClassInitializerInThread).Start(); 
     new Thread(ClassInitializerInThread).Start(); 
     new Thread(ClassInitializerInThread).Start(); 
    } 

उपरोक्त कोड नीचे दिए गए परिणामों का उत्पादन करता है।

10: ClassInitializerInThread() starting. 
11: ClassInitializerInThread() starting. 
12: ClassInitializerInThread() starting. 
InitializerTest() starting. 
InitializerTest() finished. 
11: ClassInitializerInThread() status = _x = 2 
The thread 0x2650 has exited with code 0 (0x0). 
10: ClassInitializerInThread() status = _x = 2 
The thread 0x1f50 has exited with code 0 (0x0). 
12: ClassInitializerInThread() status = _x = 2 
The thread 0x73c has exited with code 0 (0x0). 

हालांकि स्थिर निर्माता को चलाने के लिए काफी समय लगा, अन्य धागे बंद हो गए और इंतजार कर रहे थे। सभी थ्रेड स्थिर कन्स्ट्रक्टर के नीचे _x सेट के मान को पढ़ते हैं।

संबंधित मुद्दे