मुझे आलसी <टी> का उपयोग कब करना चाहिए?


327

मुझे यह लेख मिला Lazy: C # 4.0 में आलस्य - आलसी

आलसी वस्तुओं का उपयोग करने के लिए सबसे अच्छा प्रदर्शन करने के लिए सबसे अच्छा अभ्यास क्या है? क्या कोई मुझे वास्तविक एप्लिकेशन में व्यावहारिक उपयोग के लिए इंगित कर सकता है? दूसरे शब्दों में, मुझे इसका उपयोग कब करना चाहिए?


42
बदलता है: get { if (foo == null) foo = new Foo(); return foo; }। और इसका उपयोग करने के लिए संभावित स्थानों के zillions हैं ...
Kirk Woll

57
ध्यान दें कि get { if (foo == null) foo = new Foo(); return foo; }थ्रेड-सुरक्षित नहीं है, जबकि Lazy<T>डिफ़ॉल्ट रूप से थ्रेड-सुरक्षित है।
मैथ्यू

23
MSDN से: महत्वपूर्ण: आलसी आरंभीकरण थ्रेड-सुरक्षित है, लेकिन यह निर्माण के बाद ऑब्जेक्ट की सुरक्षा नहीं करता है। जब तक कि थ्रेड सुरक्षित नहीं है, आपको इसे एक्सेस करने से पहले ऑब्जेक्ट को लॉक करना होगा।
पेड्रो.किड मारा

जवाबों:


237

आप आम तौर पर इसका उपयोग तब करते हैं जब आप पहली बार इसके वास्तव में उपयोग किए जाने वाले किसी चीज़ को इंस्टेंट करना चाहते हैं। यह लागत को तब तक विलंबित करता है जब तक कि इसकी आवश्यकता तब न हो जब तक कि लागत को हमेशा के लिए समाप्त न कर दिया जाए।

आमतौर पर यह बेहतर होता है जब ऑब्जेक्ट का उपयोग किया जा सकता है या नहीं किया जा सकता है और इसके निर्माण की लागत गैर-तुच्छ है।


121
क्यों नहीं हमेशा आलसी का उपयोग करें?
TrueOf42

44
यह पहले उपयोग में लागत लगाता है और ऐसा करने के लिए कुछ लॉकिंग ओवरहेड (या यार्न थ्रेड सेफ्टी नहीं) का उपयोग कर सकता है। इस प्रकार, इसे सावधानी से चुना जाना चाहिए और जब तक ज़रूरत न हो इसका उपयोग नहीं किया जाना चाहिए।
जेम्स माइकल हरे

3
जेम्स आप पर विस्तार कर सकते हैं "और निर्माण की लागत गैर-तुच्छ है"? मेरे मामले में मेरी कक्षा में 19 संपत्तियां हैं और ज्यादातर मामलों में केवल 2 या 3 को ही देखना होगा। इसलिए मैं प्रत्येक संपत्ति का उपयोग करने पर विचार कर रहा हूं Lazy<T>। हालांकि, प्रत्येक संपत्ति बनाने के लिए मैं एक रैखिक प्रक्षेप (या एक बिलिनियर प्रक्षेप) कर रहा हूं जो काफी तुच्छ है, लेकिन कुछ लागत है। (क्या आप यह सुझाव देने जा रहे हैं कि मैं स्वयं जाकर प्रयोग करूँ?)
बेन

3
जेम्स, मेरी अपनी सलाह लेते हुए मैंने अपना एक प्रयोग किया। मेरी पोस्ट देखें
बेन

17
आप उच्च थ्रूपुट, कम विलंबता सिस्टम में उपयोगकर्ता विलंबता को रोकने के लिए "सिस्टम स्टार्ट-अप" के दौरान सब कुछ इनिशियलाइज़ / इंस्टेंट करना चाह सकते हैं। यह कई कारणों में से एक है "आलसी" का हमेशा उपयोग न करना।
डेरिक

126

आपको सिंग्लेटन्स का उपयोग करने से बचने की कोशिश करनी चाहिए, लेकिन यदि आपको कभी भी ज़रूरत पड़े, तो Lazy<T>आलसी, थ्रेड-सुरक्षित सिंगलटन को लागू करना आसान बनाता है:

public sealed class Singleton
{
    // Because Singleton's constructor is private, we must explicitly
    // give the Lazy<Singleton> a delegate for creating the Singleton.
    static readonly Lazy<Singleton> instanceHolder =
        new Lazy<Singleton>(() => new Singleton());

    Singleton()
    {
        // Explicit private constructor to prevent default public constructor.
        ...
    }

    public static Singleton Instance => instanceHolder.Value;
}

38
मुझे पढने से नफरत है आपको सिंगलेट्स का उपयोग करने से बचने की कोशिश करनी चाहिए जब मैं उनका उपयोग कर रहा हूं: डी ... अब मुझे यह सीखने की आवश्यकता है कि मुझे उनसे बचने की कोशिश क्यों करनी चाहिए: डी
बार्ट कैलिक्सो

24
जब Microsoft उनके उदाहरणों में उनका उपयोग करना बंद कर देता है तो मैं सिंग्लेटन्स का उपयोग करना बंद कर दूंगा।
eaglei22

4
मैं सिंगलेट्स से बचने की आवश्यकता की धारणा से असहमत हूं। निर्भरता इंजेक्शन प्रतिमान का पालन करते समय, यह किसी भी तरह से बात नहीं करना चाहिए। आदर्श रूप से, आपकी सभी निर्भरताएँ केवल एक बार बनाई जानी चाहिए। यह उच्च लोड परिदृश्यों में GC पर दबाव को कम करता है। इसलिए, उन्हें कक्षा के भीतर से एक सिंगलटन बनाना ही ठीक है। अधिकांश (यदि सभी नहीं) आधुनिक डीआई कंटेनर आप इसे चुन सकते हैं जिस तरह से आप चुनते हैं।
ली ग्रिसोम

1
आपको उस तरह के सिंगलटन पैटर्न का उपयोग नहीं करना है, इसके बजाय किसी भी डी कंटेनर का उपयोग करके सिंगलटन के लिए अपनी कक्षा को कॉन्फ़िगर करें। कंटेनर आपके लिए ओवरहेड की देखभाल करेगा।
विवेकदेव

हर चीज का एक उद्देश्य होता है, ऐसी परिस्थितियां होती हैं, जहां सिंगलटन एक अच्छा तरीका है और ऐसी स्थितियां जहां वह नहीं है :)।
हॉकजी

86

एक बेहतरीन वास्तविक दुनिया का उदाहरण जहां आलसी लोडिंग काम में आती है, वह है ORM's (ऑब्जेक्ट रिलेशन मैपर) जैसे कि एंटिटी फ्रेमवर्क और NHibernate।

मान लें कि आपके पास एक इकाई ग्राहक है जिसमें नाम, PhoneNumber, और आदेश के लिए गुण हैं। नाम और फोननंबर नियमित रूप से तार हैं, लेकिन ऑर्डर एक नेविगेशन संपत्ति है जो ग्राहक द्वारा किए गए हर आदेश की सूची देता है।

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

यह आलसी लोडिंग के लिए सही जगह है क्योंकि अगर ऑर्डर की संपत्ति आलसी है तो यह ग्राहक के सभी ऑर्डर नहीं लेगा जब तक कि आपको वास्तव में उनकी आवश्यकता न हो। आप ग्राहक वस्तुओं को केवल उनका नाम और फ़ोन नंबर प्राप्त कर सकते हैं, जबकि ऑर्डर की संपत्ति धैर्यपूर्वक सो रही है, जब आपको इसकी आवश्यकता हो तो तैयार रहें।


34
खराब उदाहरण, जैसे कि आलसी लोडिंग आमतौर पर पहले से ही ओआरएम में बनाया गया है। आपको आलसी लोडिंग प्राप्त करने के लिए अपने POCOs में Lazy <T> मान जोड़ना शुरू नहीं करना चाहिए, लेकिन ऐसा करने के लिए ORM- विशिष्ट तरीके का उपयोग करें।
डायलाटन

56
@Dyna यह उदाहरण एक ORM की अंतर्निहित आलसी लोडिंग का उल्लेख कर रहा है क्योंकि मुझे लगता है कि यह स्पष्ट और सरल तरीके से आलसी लोडिंग की उपयोगिता को दर्शाता है।
डेस्परटेर

यदि आप एंटिटी फ्रेमवर्क का उपयोग कर रहे हैं तो क्या किसी को अपने आलसी को लागू करना चाहिए? या ईएफ आपके लिए करता है?
जैपनीजोलिका

7
@Zapnologica EF डिफ़ॉल्ट रूप से आपके लिए यह सब करता है। वास्तव में, यदि आप उत्सुक लोडिंग (आलसी लोडिंग के विपरीत) चाहते हैं, तो आपको स्पष्ट रूप से ईएफ का उपयोग करके बताना होगा Db.Customers.Include("Orders")। इसके कारण उस आदेश को उस समय निष्पादित किया जाएगा जब Customer.Ordersसंपत्ति का उपयोग पहली बार किया गया हो। आलसी लोडिंग को DbContext के माध्यम से भी निष्क्रिय किया जा सकता है।
डेस्परर्ट

2
वास्तव में यह एक अच्छा उदाहरण है, क्योंकि कोई व्यक्ति डैपर जैसी किसी चीज़ का उपयोग करते समय इस कार्यक्षमता को जोड़ना चाहेगा।
tbone

41

मैं Lazy<T>अपने स्वयं के कोड के प्रदर्शन को बेहतर बनाने में मदद करने के लिए (और इसके बारे में थोड़ा और जानने के लिए) गुणों का उपयोग करने पर विचार कर रहा हूं । मैं इसका उपयोग करने के बारे में उत्तरों की तलाश में यहां आया था लेकिन ऐसा लगता है कि हर जगह मैं वहां जाता हूं जैसे वाक्यांश:

किसी बड़ी या संसाधन-गहन वस्तु के निर्माण को टालने के लिए, या संसाधन-गहन कार्य के निष्पादन के लिए आलसी आरंभीकरण का उपयोग करें, खासकर जब प्रोग्राम के जीवनकाल में ऐसा निर्माण या निष्पादन नहीं हो सकता है।

से MSDN लेज़ी <टी> कक्षा

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

अंत में मैंने अपना खुद का परीक्षण करने का फैसला किया और मुझे लगा कि मैं यहां परिणाम साझा करूंगा। दुर्भाग्य से मैं वास्तव में इस तरह के परीक्षण करने में विशेषज्ञ नहीं हूं और इसलिए मुझे उन टिप्पणियों को प्राप्त करने की खुशी है जो सुधार का सुझाव देते हैं।

विवरण

मेरे मामले के लिए, मुझे यह देखने में विशेष रूप से दिलचस्पी थी कि क्या आलसी गुण मेरे कोड के एक हिस्से को बेहतर बनाने में मदद कर सकता है जो बहुत अधिक प्रक्षेप करता है (ज्यादातर इसका अप्रयुक्त होना) और इसलिए मैंने एक परीक्षण बनाया है जिसमें 3 दृष्टिकोणों की तुलना की गई है।

मैंने प्रत्येक दृष्टिकोण के लिए 20 परीक्षण गुणों (उन्हें टी-गुण कहते हैं) के साथ एक अलग परीक्षण वर्ग बनाया।

  • GetInterp क्लास: हर बार एक टी-प्रॉपर्टी मिलने पर लीनियर इंटरपोलेशन चलता है।
  • InitInterp क्लास: कंस्ट्रक्टर में प्रत्येक के लिए रैखिक प्रक्षेप को चलाकर टी-गुणों को आरम्भ करता है । यह रिटर्न सिर्फ दोगुना मिलता है।
  • InitLazy Class: टी-प्रॉपर्टीज़ को Lazy प्रॉपर्टीज़ के रूप में सेट करता है ताकि प्रॉपर्टी मिलते ही लीनियर इंटरपोलेशन एक बार चला जाए। बाद में हो जाता है बस एक पहले से ही गणना की दोहरी वापसी चाहिए।

परीक्षण के परिणाम एमएस में मापा जाता है और 50 इंस्टेंटिएशन या 20 संपत्ति प्राप्त करने का औसत होता है। प्रत्येक परीक्षण तब 5 बार चलाया गया था।

टेस्ट 1 परिणाम: इंस्टेंटिएशन (औसतन 50 इंस्टेंटिएशन)

Class      1        2        3        4        5        Avg       %
------------------------------------------------------------------------
GetInterp  0.005668 0.005722 0.006704 0.006652 0.005572 0.0060636 6.72
InitInterp 0.08481  0.084908 0.099328 0.098626 0.083774 0.0902892 100.00
InitLazy   0.058436 0.05891  0.068046 0.068108 0.060648 0.0628296 69.59

टेस्ट 2 के परिणाम: पहला स्थान (औसतन 20 संपत्ति मिलती है)

Class      1        2        3        4        5        Avg       %
------------------------------------------------------------------------
GetInterp  0.263    0.268725 0.31373  0.263745 0.279675 0.277775 54.38
InitInterp 0.16316  0.161845 0.18675  0.163535 0.173625 0.169783 33.24
InitLazy   0.46932  0.55299  0.54726  0.47878  0.505635 0.510797 100.00

परीक्षण 3 परिणाम: दूसरा प्राप्त (औसत 20 संपत्ति मिलता है)

Class      1        2        3        4        5        Avg       %
------------------------------------------------------------------------
GetInterp  0.08184  0.129325 0.112035 0.097575 0.098695 0.103894 85.30
InitInterp 0.102755 0.128865 0.111335 0.10137  0.106045 0.110074 90.37
InitLazy   0.19603  0.105715 0.107975 0.10034  0.098935 0.121799 100.00

टिप्पणियों

GetInterpउम्मीद के अनुसार सबसे तेज है क्योंकि कुछ भी नहीं कर रहा है। InitLazyयह InitInterpसुझाव देने की तुलना में त्वरित करने के लिए तेज़ है कि आलसी गुणों को स्थापित करने में ओवरहेड मेरी रैखिक प्रक्षेप गणना की तुलना में तेज़ है। हालाँकि, मैं यहाँ थोड़ा उलझन में हूँ क्योंकि InitInterp20 लीनियर इंटरपोलेशन करना चाहिए (इसे टी-प्रॉपर्टीज़ सेट करने के लिए) लेकिन यह केवल 0.09 ms को तुरंत (टेस्ट 1) ले रहा है, जिसकी तुलना में GetInterpसिर्फ एक रैखिक इंटरपोल करने के लिए 0.28 ms लगते हैं। पहली बार (परीक्षण 2), और दूसरी बार (परीक्षण 3) करने के लिए 0.1 एमएस।

यह लेता है InitLazyलगभग 2 बार से अधिक समय GetInterpएक संपत्ति के लिए पहली बार प्राप्त करने के लिए है, जबकि InitInterp, सबसे तेजी से है, क्योंकि यह इन्स्टेन्शियशन दौरान उसके गुण आबादी। (कम से कम यह है कि यह क्या किया जाना चाहिए था, लेकिन यह एक एकल रैखिक प्रक्षेप की तुलना में तात्कालिकता परिणाम इतना तेज क्यों था? जब वास्तव में यह इन प्रक्षेप कर रहा है?)

दुर्भाग्य से ऐसा लगता है कि मेरे परीक्षणों में कुछ स्वचालित कोड अनुकूलन चल रहा है। GetInterpकिसी संपत्ति को पहली बार प्राप्त करने के लिए उसी समय लेना चाहिए जैसा कि वह दूसरी बार करता है, लेकिन यह 2x से अधिक तेजी से दिखा रहा है। ऐसा लगता है कि यह अनुकूलन अन्य वर्गों को भी प्रभावित कर रहा है क्योंकि वे सभी परीक्षण 3 के लिए समान समय ले रहे हैं। हालांकि, इस तरह के अनुकूलन मेरे स्वयं के उत्पादन कोड में भी हो सकते हैं जो एक महत्वपूर्ण विचार भी हो सकता है।

निष्कर्ष

जबकि कुछ परिणाम अपेक्षित हैं, कोड अनुकूलन के कारण संभवतः कुछ बहुत ही अप्रत्याशित अप्रत्याशित परिणाम भी हैं। यहां तक ​​कि ऐसी कक्षाओं के लिए जो यह देखते हैं कि वे निर्माणकर्ता में बहुत काम कर रहे हैं, तात्कालिक परिणाम बताते हैं कि वे अभी भी एक डबल संपत्ति प्राप्त करने की तुलना में बनाने में बहुत तेज हो सकते हैं। हालांकि इस क्षेत्र के विशेषज्ञ अधिक अच्छी तरह से टिप्पणी करने और जांच करने में सक्षम हो सकते हैं, मेरी व्यक्तिगत भावना यह है कि मुझे इस परीक्षण को फिर से करने की आवश्यकता है, लेकिन मेरे उत्पादन कोड पर यह जांचने के लिए कि वहां किस प्रकार के अनुकूलन हो सकते हैं। हालाँकि, मैं उम्मीद कर रहा हूँ कि InitInterpजाने का रास्ता हो सकता है।


26
शायद आपको अपना आउटपुट पुन: प्रस्तुत करने के लिए अपना परीक्षण कोड पोस्ट करना चाहिए, क्योंकि आपके कोड को जाने बिना कुछ भी सुझाना कठिन होगा
WiiMaxx

1
मेरा मानना ​​है कि मुख्य व्यापार-बंद मेमोरी उपयोग (आलसी) और सीपीयू उपयोग (नहीं-आलसी) के बीच है। क्योंकि lazyकुछ अतिरिक्त बुक-कीपिंग InitLazyकरनी होती है, अन्य समाधानों की तुलना में अधिक मेमोरी का उपयोग करेगा। इसमें प्रत्येक एक्सेस पर एक मामूली प्रदर्शन हिट हो सकता है, जबकि यह जांचता है कि इसका पहले से ही मूल्य है या नहीं; चालाक चालें उस ओवरहेड को हटा सकती हैं, लेकिन इसे आईएल में विशेष समर्थन की आवश्यकता होगी। (हास्केल हर आलसी मूल्य को एक फ़ंक्शन कॉल करके ऐसा करता है; एक बार मूल्य उत्पन्न होने पर, इसे एक फ़ंक्शन के साथ बदल दिया जाता है जो हर बार उस मूल्य को लौटाता है।)
jpaugh

14

सिर्फ मैथ्यू द्वारा पोस्ट किए गए उदाहरण पर इंगित करने के लिए

public sealed class Singleton
{
    // Because Singleton's constructor is private, we must explicitly
    // give the Lazy<Singleton> a delegate for creating the Singleton.
    private static readonly Lazy<Singleton> instanceHolder =
        new Lazy<Singleton>(() => new Singleton());

    private Singleton()
    {
        ...
    }

    public static Singleton Instance
    {
        get { return instanceHolder.Value; }
    }
}

आलसी पैदा होने से पहले हमने इसे इस तरह किया होगा:

private static object lockingObject = new object();
public static LazySample InstanceCreation()
{
    if(lazilyInitObject == null)
    {
         lock (lockingObject)
         {
              if(lazilyInitObject == null)
              {
                   lazilyInitObject = new LazySample ();
              }
         }
    }
    return lazilyInitObject ;
}

6
मैं हमेशा इसके लिए एक IoC कंटेनर का उपयोग करता हूं।
जौइन

1
मैं इसके लिए आईओसी कंटेनर पर विचार करने पर दृढ़ता से सहमत हूं। यदि आप एक सरल आलसी इनिशियलाइज्ड ऑब्जेक्ट सिंगलटन चाहते हैं, तो यह भी विचार करें कि यदि आपको थ्रेड सेफ होने के लिए इसकी आवश्यकता नहीं है, तो इसे मैन्युअल रूप से इफ के साथ करना सबसे अच्छा हो सकता है, क्योंकि लेज़ी खुद को कैसे संभालता है।
थुलानी चिवंडिकावा

12

MSDN से:

किसी बड़ी या संसाधन-गहन वस्तु के निर्माण को टालने के लिए या संसाधन-गहन कार्य के निष्पादन को स्थगित करने के लिए आलसी के उदाहरण का उपयोग करें, खासकर जब प्रोग्राम के जीवनकाल के दौरान ऐसी रचना या निष्पादन नहीं हो सकता है।

जेम्स माइकल हरे के जवाब के अलावा, आलसी आपके मूल्य के थ्रेड-सुरक्षित आरंभीकरण प्रदान करता है। इस वर्ग के लिए विभिन्न प्रकार के थ्रेड सुरक्षा मोड का वर्णन करते हुए LazyThreadSafetyMode एन्यूमरेशन MSDN प्रविष्टि पर एक नज़र डालें ।


-2

आलसी लोडिंग आर्किटेक्चर को समझने के लिए आपको यह उदाहरण देखना चाहिए

private readonly Lazy<List<int>> list = new Lazy<List<int>>(() =>
{
    List<int> configList = new List<int>(Thread.CurrentThread.ManagedThreadId);
    return configList;
});
public void Execute()
{
    list.Value.Add(0);
    if (list.IsValueCreated)
    {
        list.Value.Add(1);
        list.Value.Add(2);

        foreach (var item in list.Value)
        {
            Console.WriteLine(item);
        }
    }
    else
    {
        Console.WriteLine("Value not created");
    }
}

-> आउटपुट -> 0 1 2

लेकिन अगर यह कोड "list.Value.Add (0);" न लिखें;

आउटपुट -> मूल्य नहीं बनाया गया

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.