थ्रेड सेफ सी # सिंगलटन पैटर्न


79

यहाँ दिए गए दस्तावेज के रूप में सिंगलटन पैटर्न के बारे में मेरे कुछ प्रश्न हैं: http://msdn.microsoft.com/en-us/library/ff650316.aspx

निम्नलिखित कोड लेख से एक उद्धरण है:

using System;

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}

विशेष रूप से, उपरोक्त उदाहरण में, लॉक से पहले और बाद में दो बार अशक्त करने के लिए उदाहरण की तुलना करने की आवश्यकता है? क्या यह आवश्यक है? पहले ताला क्यों नहीं लगाया और तुलना करें?

निम्नलिखित को सरल बनाने में कोई समस्या है?

   public static Singleton Instance
   {
      get 
      {
        lock (syncRoot) 
        {
           if (instance == null) 
              instance = new Singleton();
        }

         return instance;
      }
   }

क्या प्रदर्शन करना महंगा है?


17
एक तरफ, जॉन स्कीट ने सिंगलेट्स
Arran

आलसी स्थिर init बेहतर होगा ...
मिच गेहूं

1
मुझे यहाँ स्पष्टीकरण के साथ अन्य उदाहरण भी मिले: csharpindepth.com/Articles/General/Singleton.aspx
सर्ज

वास्तव में जावा दुनिया के लिए यहाँ एक ही सवाल है
आरबीटी

जवाबों:


133

सरल पॉइंटर चेक की तुलना में लॉक का प्रदर्शन करना बहुत महंगा है instance != null

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

इसे इस तरह से सोचें: एक नंगे nullचेक (बिना lock) आपको केवल एक सही प्रयोग करने योग्य उत्तर देने की गारंटी दी जाती है, जब वह उत्तर "हां, वस्तु पहले से ही निर्मित है"। लेकिन यदि उत्तर "अभी तक निर्माण नहीं किया गया है" तो आपके पास पर्याप्त जानकारी नहीं है क्योंकि जो आप वास्तव में जानना चाहते थे वह यह है कि "अभी तक इसका निर्माण नहीं किया गया है और कोई अन्य धागा शीघ्र ही इसका निर्माण करने का इरादा नहीं है "। इसलिए आप बाहरी जांच को बहुत ही प्रारंभिक प्रारंभिक परीक्षण के रूप में उपयोग करते हैं और आप उचित, बग-मुक्त लेकिन "महंगी" प्रक्रिया (लॉक तो चेक) शुरू करते हैं, केवल यदि उत्तर "नहीं" है।

उपरोक्त कार्यान्वयन अधिकांश मामलों के लिए पर्याप्त है, लेकिन इस बिंदु पर C # में सिंगलटन पर जॉन स्कीट के लेख को पढ़ने और पढ़ने के लिए एक अच्छा विचार है जो अन्य विकल्पों का भी मूल्यांकन करता है।


1
उपयोगी लिंक के साथ जानकारीपूर्ण प्रतिक्रिया के लिए थैंक्यू। बहुत सराहना की।
वेन Phipps

डबल-चेकिंग लॉकिंग - लिंक अब काम नहीं करता है।
एल मैक

मुझे खेद है, मेरा मतलब दूसरे से था।
एल मैक

1
@ElMac: स्कीट की वेबसाइट एटीएम से नीचे है, यह नियत समय में वापस आ जाएगी। मैं इसे ध्यान में रखूंगा और सुनिश्चित करूंगा कि लिंक अभी भी काम करता है जब यह ऊपर आता है, धन्यवाद।
जॉन

3
.NET 4.0 से Lazy<T>यह काम पूरी तरह से हो रहा है।
इलियाब्रीव

34

Lazy<T>संस्करण:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy
        = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance
        => lazy.Value;

    private Singleton() { }
}

.NET 4 और C # 6.0 (VS2015) या नए की आवश्यकता है।


मुझे "System.MissingMemberException" मिल रही है: 'lazily-initialized टाइप का कोई पब्लिक, पैरामीटरलेस कंस्ट्रक्टर नहीं होता है।' 'इस कोड के साथ .Net 4.6.1 / C # 6.
ttugates

@ttugates, आप सही हैं, धन्यवाद। कोड आलसी वस्तु के लिए एक मूल्य कारखाना कॉलबैक के साथ अद्यतन किया गया।
औरसा

14

एक ताला प्रदर्शन: काफी सस्ता (अभी भी एक शून्य परीक्षण की तुलना में अधिक महंगा)।

जब एक और धागा होता है, तो लॉक करना: आपको लॉक करते समय जो कुछ भी करना होता है, उसकी कीमत आपको अपने समय में मिल जाती है।

एक ताला का प्रदर्शन जब एक और धागा होता है, और दर्जनों अन्य धागे भी उस पर इंतजार कर रहे होते हैं: अपंग।

प्रदर्शन कारणों के लिए, आप हमेशा ऐसे ताले लगाना चाहते हैं जो एक और धागा चाहता है, संभव समय की सबसे छोटी अवधि के लिए।

बेशक, संकीर्ण की तुलना में "व्यापक" तालों के बारे में तर्क करना आसान है, इसलिए यह उनके साथ शुरू करने और आवश्यकतानुसार अनुकूलन करने के लायक है, लेकिन कुछ मामले हैं जो हम अनुभव और परिचितता से सीखते हैं जहां एक संकरा पैटर्न फिट बैठता है।

(संयोग से, यदि आप संभवतः केवल उपयोग कर सकते हैं private static volatile Singleton instance = new Singleton()या यदि आप संभवतः केवल एकल का उपयोग नहीं कर सकते हैं लेकिन इसके बजाय एक स्थिर वर्ग का उपयोग कर सकते हैं, तो इन चिंताओं के संबंध में दोनों बेहतर हैं)।


1
मुझे आपकी सोच यहां बहुत पसंद है। इसे देखने का एक शानदार तरीका है। काश मैं दो उत्तर या +5 इस एक को स्वीकार कर सकता, बहुत धन्यवाद
वेन Phipps

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

आपके विकल्प पर, volatileयह आवश्यक नहीं है, हालांकि यह होना चाहिए readonly। देखें stackoverflow.com/q/12159698/428724
वीजेन

7

कारण है प्रदर्शन। यदि instance != null(जो हमेशा बहुत पहली बार को छोड़कर) होगा, तो महंगा करने की आवश्यकता नहीं है lock: एक साथ आरंभिक सिंगलटन तक पहुंचने वाले दो धागे अनावश्यक रूप से सिंक्रनाइज़ किए जाएंगे।


4

लगभग हर मामले में (यानी: बहुत पहले वाले मामलों को छोड़कर) सभी मामले instanceशून्य नहीं होंगे। ताला खोलना एक साधारण जाँच की तुलना में अधिक महंगा है, इसलिए instanceताला लगाने से पहले एक बार जाँच कर लेना एक अच्छा और मुफ्त अनुकूलन है।

इस पैटर्न को डबल-चेक लॉकिंग कहा जाता है: http://en.wikipedia.org/wiki/Double-checked_locking


3

जेफरी रिक्टर निम्नलिखित की सिफारिश करता है:



    public sealed class Singleton
    {
        private static readonly Object s_lock = new Object();
        private static Singleton instance = null;
    
        private Singleton()
        {
        }
    
        public static Singleton Instance
        {
            get
            {
                if(instance != null) return instance;
                Monitor.Enter(s_lock);
                Singleton temp = new Singleton();
                Interlocked.Exchange(ref instance, temp);
                Monitor.Exit(s_lock);
                return instance;
            }
        }
    }


उदाहरण चर अस्थिर नहीं बना रहा है, एक ही बात करता है?
20 Г И О И О

1

आप बेसब्री से एक थ्रेड-सेफ सिंगलटन उदाहरण बना सकते हैं, जो आपके आवेदन की जरूरतों पर निर्भर करता है, यह संक्षिप्त कोड है, हालांकि मैं @ और ओसा के आलसी संस्करण को पसंद करूंगा।

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

    private Singleton() { }

    public static Singleton Instance()
    {
        return instance;
    }
}

0

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

public sealed class Singleton
{
    private static readonly object Instancelock = new object();
    private Singleton()
    {
    }
    private static Singleton instance = null;

    public static Singleton GetInstance
    {
        get
        {
            if (instance == null)
            {
                lock (Instancelock)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}

0

सिंगलटन का एक और संस्करण जहां कोड की निम्न पंक्ति एप्लिकेशन स्टार्टअप के समय सिंग्लटन उदाहरण बनाती है।

private static readonly Singleton singleInstance = new Singleton();

यहां CLR (सामान्य भाषा रनटाइम) ऑब्जेक्ट इनिशियलाइज़ेशन और थ्रेड सेफ्टी का ध्यान रखेगा। इसका मतलब है कि हमें बहुपरत वातावरण के लिए थ्रेड सुरक्षा को संभालने के लिए किसी भी कोड को स्पष्ट रूप से लिखने की आवश्यकता नहीं होगी।

"सिंगलटन डिज़ाइन पैटर्न में ईगर लोडिंग एक ऐसी प्रक्रिया नहीं है, जिसमें हमें मांग के बजाय एप्लिकेशन स्टार्ट-अप के समय सिंगलटन ऑब्जेक्ट को इनिशियलाइज़ करने की आवश्यकता होती है और इसे भविष्य में उपयोग में लाने के लिए तैयार रखें।"

public sealed class Singleton
    {
        private static int counter = 0;
        private Singleton()
        {
            counter++;
            Console.WriteLine("Counter Value " + counter.ToString());
        }
        private static readonly Singleton singleInstance = new Singleton(); 

        public static Singleton GetInstance
        {
            get
            {
                return singleInstance;
            }
        }
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }

मुख्य से:

static void Main(string[] args)
        {
            Parallel.Invoke(
                () => PrintTeacherDetails(),
                () => PrintStudentdetails()
                );
            Console.ReadLine();
        }
        private static void PrintTeacherDetails()
        {
            Singleton fromTeacher = Singleton.GetInstance;
            fromTeacher.PrintDetails("From Teacher");
        }
        private static void PrintStudentdetails()
        {
            Singleton fromStudent = Singleton.GetInstance;
            fromStudent.PrintDetails("From Student");
        }

अच्छा विकल्प लेकिन उस प्रश्न का उत्तर नहीं देता है जो प्रश्न में उल्लिखित विशिष्ट कार्यान्वयन में लॉकिंग चेक के बारे में था
वेन Phipps

सीधे नहीं, लेकिन एक वैकल्पिक "थ्रेड सेफ सी # सिंगलटन पैटर्न" के रूप में उपयोग किया जा सकता है।
जयदीप शील

0

प्रतिबिंब प्रतिरोधी सिंगलटन पैटर्न:

public sealed class Singleton
{
    public static Singleton Instance => _lazy.Value;
    private static Lazy<Singleton, Func<int>> _lazy { get; }

    static Singleton()
    {
        var i = 0;
        _lazy = new Lazy<Singleton, Func<int>>(() =>
        {
            i++;
            return new Singleton();
        }, () => i);
    }

    private Singleton()
    {
        if (_lazy.Metadata() == 0 || _lazy.IsValueCreated)
            throw new Exception("Singleton creation exception");
    }

    public void Run()
    {
        Console.WriteLine("Singleton called");
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.