रिटर्न स्टेटमेंट लॉक के अंदर या बाहर होना चाहिए?


142

मुझे बस एहसास हुआ कि मेरे कोड में किसी जगह पर लॉक के अंदर और कुछ समय के भीतर रिटर्न स्टेटमेंट है। कौन सा सबसे अच्छा है?

1)

void example()
{
    lock (mutex)
    {
    //...
    }
    return myData;
}

2)

void example()
{
    lock (mutex)
    {
    //...
    return myData;
    }

}

मुझे किसका उपयोग करना चाहिए?


रिफ्लेक्टर फायरिंग के बारे में और कुछ IL तुलना ;-)।
पॉप कैटलिन

6
@Pop: किया - न आईएल मामले में बेहतर है - केवल सी # शैली लागू
मार्क Gravell

1
बहुत दिलचस्प है, वाह मैं आज कुछ सीखता हूं!
पोकस

जवाबों:


192

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

आईएल में अंतर दिखाने के लिए, कोड देता है:

static class Program
{
    static void Main() { }

    static readonly object sync = new object();

    static int GetValue() { return 5; }

    static int ReturnInside()
    {
        lock (sync)
        {
            return GetValue();
        }
    }

    static int ReturnOutside()
    {
        int val;
        lock (sync)
        {
            val = GetValue();
        }
        return val;
    }
}

(ध्यान दें कि मैं खुशी से तर्क दूंगा कि ReturnInsideC # का एक सरल / क्लीनर बिट है)

और आईएल देखें (रिलीज मोड आदि):

.method private hidebysig static int32 ReturnInside() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 CS$1$0000,
        [1] object CS$2$0001)
    L_0000: ldsfld object Program::sync
    L_0005: dup 
    L_0006: stloc.1 
    L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
    L_000c: call int32 Program::GetValue()
    L_0011: stloc.0 
    L_0012: leave.s L_001b
    L_0014: ldloc.1 
    L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
    L_001a: endfinally 
    L_001b: ldloc.0 
    L_001c: ret 
    .try L_000c to L_0014 finally handler L_0014 to L_001b
} 

method private hidebysig static int32 ReturnOutside() cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 val,
        [1] object CS$2$0000)
    L_0000: ldsfld object Program::sync
    L_0005: dup 
    L_0006: stloc.1 
    L_0007: call void [mscorlib]System.Threading.Monitor::Enter(object)
    L_000c: call int32 Program::GetValue()
    L_0011: stloc.0 
    L_0012: leave.s L_001b
    L_0014: ldloc.1 
    L_0015: call void [mscorlib]System.Threading.Monitor::Exit(object)
    L_001a: endfinally 
    L_001b: ldloc.0 
    L_001c: ret 
    .try L_000c to L_0014 finally handler L_0014 to L_001b
}

तो आईएल स्तर पर वे [कुछ नाम दे] या ले रहे हैं समान (मैं कुछ सीखा; - पीपी)। जैसे, केवल समझदार तुलना स्थानीय कोडिंग शैली का (अत्यधिक व्यक्तिपरक) कानून है ... मैं ReturnInsideसरलता के लिए पसंद करता हूं , लेकिन मैं इसके बारे में उत्साहित नहीं होता।


15
मैंने रेड गेट के .NET रिफ्लेक्टर ((:: Lutz Roeder's .NET रिफ्लेक्टर)) का उपयोग किया है, लेकिन ILDASM भी करेगा।
मार्क Gravell

1
परावर्तक के सबसे शक्तिशाली पहलुओं में से एक है कि आप कर सकते हैं अपनी पसंद की भाषा (सी #, वीबी, डेल्फी, एम सी ++, क्रोम, आदि) के लिए वास्तव में जुदा आईएल
मार्क Gravell

3
अपने सरल उदाहरण के लिए आईएल एक ही रहता है, लेकिन ऐसा शायद इसलिए है क्योंकि आप केवल एक स्थिर मान लौटाते हैं? मेरा मानना ​​है कि वास्तविक जीवन परिदृश्यों के परिणाम भिन्न हो सकते हैं, और समानांतर सूत्र मूल्य को संशोधित करने से पहले एक-दूसरे के लिए समस्याएँ पैदा कर सकते हैं, इसे वापस करने से पहले यह विवरण लॉक ब्लॉक के बाहर होता है। खतरनाक!
Torbjørn

@MarcGravell: मैं आपकी पोस्ट को देखते हुए ही आपकी पोस्ट पर आया था और आपके उत्तर को पढ़ने के बाद भी, मुझे अभी भी निम्नलिखित के बारे में निश्चित नहीं है: क्या ऐसी कोई भी परिस्थितियां हैं जहां बाहरी दृष्टिकोण का उपयोग करके थ्रेड-सुरक्षित तर्क को तोड़ा जा सकता है। मैं यह पूछता हूं क्योंकि मैं वापसी का एक बिंदु पसंद करता हूं और इसके थ्रेड-सुरक्षा के बारे में 'FEEL' अच्छा नहीं करता। हालांकि, यदि आईएल एक ही है, तो मेरी चिंता वैसे भी मूट होनी चाहिए।
राहील खान

1
@ राहीलखन नं, कोई नहीं; वह एक जैसे है। IL स्तर पर, आप किसी क्षेत्र के अंदर नहीं जा सकतेret.try
मार्क Gravell

42

इससे कोई फर्क नहीं पड़ता; वे दोनों संकलक द्वारा एक ही चीज़ के लिए अनुवादित हैं।

स्पष्ट करने के लिए, या तो प्रभावी रूप से निम्नलिखित शब्दार्थ के साथ किसी चीज़ का अनुवाद किया जाता है:

T myData;
Monitor.Enter(mutex)
try
{
    myData= // something
}
finally
{
    Monitor.Exit(mutex);
}

return myData;

1
कि ट्राई / अंत में का सच है, ठीक है - हालांकि, ताला बाहर वापसी अभी भी अतिरिक्त स्थानीय लोगों जो दूर अनुकूलित नहीं किया जा सकता है की आवश्यकता है - और अधिक कोड लेता है ...
मार्क Gravell

3
आप एक कोशिश ब्लॉक से वापस नहीं आ सकते हैं; इसे ".leave" ऑप-कोड के साथ समाप्त होना चाहिए। इसलिए उत्सर्जित CIL दोनों ही मामलों में समान होना चाहिए।
ग्रेग बीच 21

3
आप सही हैं - मैंने सिर्फ IL को देखा है (अपडेट की गई पोस्ट देखें)। मैं कुछ ;-p सीखा
मार्क Gravell

2
कूल, दुर्भाग्य से मैंने दर्दनाक घंटों से सीखा है। कोशिश ब्लॉकों में .ret से-कोड्स को निकालने की कोशिश कर रहे हैं और सीएलआर को मेरे गतिशील तरीकों को लोड करने से मना कर रहे हैं :-(
ग्रेग बीच

मुझसे बयां हो सकता है; मैंने काफी मात्रा में प्रतिबिंबन किया है। स्वीकार करें, लेकिन मैं आलसी हूं; जब तक मैं कुछ के बारे में निश्चित नहीं हूं, मैं C # में प्रतिनिधि कोड लिखता हूं और फिर IL को देखता हूं। लेकिन यह आश्चर्यजनक है कि आप कितनी जल्दी आईएल के संदर्भ में सोचना शुरू कर देते हैं (यानी स्टैक को सीक्वेंस करना)।
मार्क Gravell

28

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


4
यह सही है, एक बिंदु अन्य उत्तरदाताओं को याद आ रहा है। उनके द्वारा किए गए सरल नमूने एक ही आईएल का उत्पादन कर सकते हैं, लेकिन अधिकांश वास्तविक जीवन के परिदृश्यों के लिए ऐसा नहीं है।
Torbjørn

4
Im ने अन्य उत्तरों को आश्चर्यचकित किया और इस बारे में बात नहीं की
अक्षत अग्रवाल

5
इस नमूने में वे रिटर्न मान को स्टोर करने के लिए स्टैक चर का उपयोग करने के बारे में बात कर रहे हैं, अर्थात केवल लॉक के बाहर रिटर्न स्टेटमेंट और निश्चित रूप से चर घोषणा। दूसरे धागे में एक और स्टैक होना चाहिए और इस तरह कोई नुकसान नहीं पहुंचा सकता है, क्या मैं सही हूं?
गिलर्मो रफ़िनो

3
मुझे नहीं लगता कि यह एक मान्य बिंदु है, क्योंकि एक और धागा रिटर्न कॉल और मुख्य थ्रेड पर चर के लिए वापसी मूल्य के वास्तविक असाइनमेंट के बीच मूल्य को अपडेट कर सकता है। लौटाए जा रहे मूल्य को वर्तमान वास्तविक मूल्य के साथ या तो बदला नहीं जा सकता है या निरंतरता की गारंटी दी जा सकती है। सही?
उरोच

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

5

निर्भर करता है,

मैं यहां अनाज के खिलाफ जा रहा हूं। मैं आम तौर पर ताला के अंदर लौट जाता।

आमतौर पर चर mydata एक स्थानीय चर है। मैं स्थानीय वैरिएबल घोषित करने का शौकीन हूं, जबकि मैं उन्हें शुरुआती करता हूं। मेरे पास अपने लॉक के बाहर अपने रिटर्न वैल्यू को इनिशियलाइज़ करने के लिए शायद ही कभी डेटा होता है।

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

void example() { 
    int myData;
    lock (foo) { 
        myData = ...;
    }
    return myData
}

बनाम

void example() { 
    lock (foo) {
        return ...;
    }
}

मुझे लगता है कि मामला 2 पढ़ने के लिए काफी आसान है और पेंच करने के लिए कठिन है, खासकर छोटे स्निपेट के लिए।


4

यदि आपको लगता है कि बाहर का ताला बेहतर लग रहा है, लेकिन यदि आप कोड को बदलते हैं तो सावधान रहें:

return f(...)

यदि f () को लॉक किए गए लॉक के साथ कॉल करने की आवश्यकता है, तो यह स्पष्ट रूप से लॉक के अंदर होना चाहिए, क्योंकि स्थिरता के लिए लॉक के अंदर रिटर्न रखने से समझ में आता है।


1

इसके लायक क्या है, MSDN पर प्रलेखन में लॉक के अंदर से लौटने का एक उदाहरण है। यहाँ पर अन्य उत्तरों से, यह काफी समान आईएल प्रतीत होता है, लेकिन मेरे लिए, यह लॉक के अंदर से लौटने के लिए अधिक सुरक्षित लगता है क्योंकि तब आप एक रिटर्न वैरिएबल के जोखिम को किसी अन्य थ्रेड द्वारा अधिलेखित नहीं करते हैं।


0

साथी डेवलपर्स के लिए कोड को पढ़ना आसान बनाने के लिए मैं पहला विकल्प सुझाऊंगा।


0

lock() return <expression> हमेशा बयान:

1) लॉक दर्ज करें

2) निर्दिष्ट प्रकार के मूल्य के लिए स्थानीय (थ्रेड-सेफ) स्टोर बनाता है,

3) द्वारा दिए गए मूल्य के साथ दुकान को भरता है <expression>,

4) निकास ताला

५) स्टोर वापस करें।

इसका अर्थ है कि मूल्य, लॉक स्टेटमेंट से लौटा, हमेशा रिटर्न से पहले "पकाया हुआ"।

चिंता मत करो lock() return, यहाँ किसी की बात मत सुनो))


-2

नोट: मेरा मानना ​​है कि यह उत्तर तथ्यात्मक रूप से सही है और मुझे आशा है कि यह मददगार भी है, लेकिन मैं इसे ठोस प्रतिक्रिया के आधार पर सुधारने के लिए हमेशा खुश हूं।

मौजूदा उत्तरों को संक्षेप और पूरक करने के लिए:

  • स्वीकार किए जाते हैं जवाब से पता चलता है कि, भले ही जो वाक्य रचना फार्म की आप अपने में चुनें सी # कोड, आईएल कोड में - रनटाइम पर है और इसलिए - returnजब तक ऐसा नहीं होता है के बाद ताला जारी किया गया है।

    • यहां तक कि रखने हालांकि return अंदरlock ब्लॉक इसलिए नियंत्रण के प्रवाह, कडाई के साथ गलत ढंग से प्रस्तुत [1] , यह वाक्य रचना है सुविधाजनक में है कि यह एक aux में वापसी मान संग्रहीत करने के लिए की जरूरत है obviates। स्थानीय चर (ब्लॉक के बाहर घोषित किया गया है, ताकि इसका उपयोग returnब्लॉक के बाहर किया जा सके ) - एडवर्ड KMETT का उत्तर देखें
  • अलग-अलग - और यह पहलू प्रश्न के लिए आकस्मिक है, लेकिन फिर भी दिलचस्पी का हो सकता है ( रिकार्डो विलमिल का उत्तर इसे संबोधित करने की कोशिश करता है, लेकिन गलत तरीके से, मुझे लगता है) - एक lockबयान के साथ एक returnबयान को संयोजित करना - यानी, returnएक ब्लॉक में मूल्य प्राप्त करना संरक्षित है। समवर्ती पहुँच - केवल सार्थक " कॉलर के दायरे में लौटाए गए मान" की रक्षा करता है यदि उसे वास्तव में एक बार प्राप्त करने के लिए सुरक्षा की आवश्यकता नहीं है , जो निम्नलिखित परिदृश्यों में लागू होता है:

    • यदि लौटाया गया मूल्य एक संग्रह से एक तत्व है जिसे केवल तत्वों को जोड़ने और हटाने के संदर्भ में सुरक्षा की आवश्यकता है , न कि तत्वों के संशोधनों के संदर्भ में स्वयं और / या ...

    • ... यदि लौटाया जा रहा मूल्य मान प्रकार या स्ट्रिंग का एक उदाहरण है ।

      • ध्यान दें कि इस स्थिति में कॉल करने वाले को मूल्य का एक स्नैपशॉट (कॉपी) [2] प्राप्त होता है - जिस समय तक कॉलर यह निरीक्षण करता है कि वह अब मूल के डेटा संरचना में वर्तमान मान नहीं हो सकता है।
    • किसी अन्य मामले में, लॉकर को कॉलर द्वारा निष्पादित किया जाना चाहिए , न कि (सिर्फ) विधि के अंदर।


[1] थियोडोर Zoulias बताते हैं कि कि तकनीकी रूप से रखने के लिए भी सही है returnअंदर try, catch, using, if, while, for, ... बयान; हालाँकि, यह उस lockकथन का विशिष्ट उद्देश्य है, जो वास्तविक नियंत्रण प्रवाह की जांच को आमंत्रित करने की संभावना है, क्योंकि इस प्रश्न का सबूत यह पूछा गया है और इस पर अधिक ध्यान दिया गया है।

[२] एक मूल्य-प्रकार के उदाहरण तक पहुँचना हमेशा एक धागा-स्थानीय बनाता है, इसकी एक-स्टैक कॉपी; भले ही तार तकनीकी रूप से संदर्भ-प्रकार के उदाहरण हैं, लेकिन वे प्रभावी रूप से मूल्य-प्रकार के उदाहरणों का व्यवहार करते हैं।


आपके उत्तर की वर्तमान स्थिति के बारे में (संशोधन 13), आप अभी भी अस्तित्व के कारणों के बारे में अनुमान लगा रहे हैं lock, और वापसी विवरण के स्थान से अर्थ प्राप्त कर रहे हैं। जो कि इस सवाल से असंबंधित है IMHO। इसके अलावा, मुझे "गड़बड़ी" का उपयोग काफी परेशान करता है। एक से लौट रहे हैं lockगलत ढंग से प्रस्तुत नियंत्रण के प्रवाह, तो एक ही एक से लौटने के लिए कहा जा सकता try, catch, using, if, while, for, और भाषा के किसी भी अन्य निर्माण। यह कहने जैसा है कि C # नियंत्रण प्रवाह गलत बयानी से ग्रस्त है। जीसस ...
थियोडोर जूलियास

"यह कहने की तरह है कि C # नियंत्रण प्रवाह की गलत व्याख्याओं से भरा हुआ है" - ठीक है, यह तकनीकी रूप से सच है, और "गलत बयानी" शब्द केवल एक मूल्य निर्णय है यदि आप इसे इस तरह से लेना चाहते हैं। के साथ try, if... मैं व्यक्तिगत रूप से इसके बारे में सोचना भी नहीं छोड़ता, लेकिन lockविशेष रूप से , के संदर्भ में , मेरे लिए सवाल पैदा हुआ - और अगर यह दूसरों के लिए भी पैदा नहीं हुआ, तो यह सवाल कभी नहीं पूछा जाएगा। , और स्वीकार किए जाते हैं जवाब सच व्यवहार की जांच करने के लिए महान लंबाई तक नहीं गया होगा।
mklement0
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.