2013-05-24 3 views
11

मैं async/await के साथ खेल रहा था जब मैं निम्नलिखित भर में आया था:टास्क <T> .Result और स्ट्रिंग संयोजन

class C 
{ 
    private static string str; 

    private static async Task<int> FooAsync() 
    { 
     str += "2"; 
     await Task.Delay(100); 
     str += "4"; 
     return 5; 
    } 

    private static void Main(string[] args) 
    { 
     str = "1"; 
     var t = FooAsync(); 
     str += "3"; 

     str += t.Result; // Line X 

     Console.WriteLine(str); 
    } 
} 

मैं परिणाम की उम्मीद "12345" हो सकता है, लेकिन यह "1235"। किसी तरह '4' खाया गया था।

int i = t.Result; 
str += i; 

तो उम्मीद "12345" परिणाम:

अगर मैं में लाइन एक्स अलग हो गए।

ऐसा क्यों है? (वीएस2012 का उपयोग)

+0

क्यों निश्चित रूप से दिलचस्प है, लेकिन यह भी ध्यान दिया जाना चाहिए यदि आप ऐसा नहीं करना चाहिए - संचालन है कि कोई ताले या अन्य सूत्रण तर्क के साथ परमाणु अनिश्चित स्थितियों में आप प्राप्त करने के लिए निश्चित है नहीं कर रहे हैं के साथ एक चर को साझा किया था। –

उत्तर

6

यह दौड़ की स्थिति है। आप अपने पास निष्पादन के दो धागे के बीच साझा चर के उपयोग को ठीक से सिंक्रनाइज़ नहीं कर रहे हैं।

आपका कोड शायद कुछ इस तरह कर रही है:

  • स्ट्रिंग सेट होने के लिए "1"
  • कॉल FooAsync
  • संलग्न 2
  • जब इंतजार मुख्य विधि कहा जाता है जारी है क्रियान्वित , FooAsync में कॉलबैक थ्रेड पूल में चलाएगा; यहां से बाहर चीजें अनिश्चित हैं।

    str += t.Result; 
    

    यहाँ यह कई छोटे आपरेशन में टूट रहा है:

  • मुख्य थ्रेड स्ट्रिंग

फिर हम दिलचस्प लाइन को पाने के लिए 3 जोड़ देता है। इसे पहले str का वर्तमान मान प्राप्त होगा। समय में इस बिंदु पर async विधि (सभी संभावनाओं में) अभी तक समाप्त नहीं हुआ है, तो यह "123" हो जाएगा। यह तो इंतजार कर रहा है कार्य को पूरा करने के लिए (क्योंकि Result एक अवरुद्ध इंतजार बलों) और स्ट्रिंग के अंत करने के लिए कार्य का परिणाम कहते हैं, इस मामले 5 में।

async कॉलबैक पकड़ा जाएगा है और फिर से सेट str के बाद मुख्य थ्रेड पहले से ही str के वर्तमान मूल्य को पकड़ लिया था, और फिर इसे str के ऊपर लिख देगा बिना यह कभी पढ़ा जा रहा के रूप में मुख्य थ्रेड इसे अधिलेखित करना जल्द ही है ।

10

ऐसा क्यों है? (वीएस2012 का उपयोग करना)

आप इसे कंसोल एप्लिकेशन में चला रहे हैं, जिसका अर्थ है कि कोई मौजूदा सिंक्रनाइज़ेशन संदर्भ नहीं है।

इस प्रकार, FooAsync() का हिस्सा await के बाद एक अलग धागे में चलता है। जब आप str += t.Result करते हैं तो आप += 4 कॉल और += t.Result के बीच प्रभावी रूप से दौड़ की स्थिति बना रहे हैं। ऐसा इसलिए है क्योंकि string += एक परमाणु ऑपरेशन नहीं है।

आप Windows प्रपत्र या WPF आवेदन के भीतर एक ही कोड को चलाने के लिए थे, तो सिंक्रनाइज़ेशन संदर्भ पर कब्जा कर लिया जाएगा और += "4" है, जो इस उसी धागे पर सभी रन का मतलब के लिए इस्तेमाल किया है, और आप यह नहीं देखना होगा मुद्दा।

+0

कैसे एक रेस स्थिति हो सकता है और यदि 'वापसी 5' होता है ** के बाद **' str + = 4' 'FooAsync' के कोड में? –

+2

@IlyaKogan क्योंकि '+ =' एक परमाणु आपरेशन नहीं है। यह कई उप-संचालन में टूट गया है जो अनिश्चित परिणामों को उत्पन्न करने के लिए (और इस मामले में) इंटरवॉवन किए जा सकते हैं। – Servy

+1

रेस स्थिति @IlyaKogan होती है क्योंकि 'str + = t.Result' प्रभावी रूप से' str = str + t.Result' है, और "STR" से पहले '+ = 4' खत्म लाई गई है। –

7

सी #के # स्टेटमेंट संकलित समय पर x = x + y; तक विस्तारित किए गए हैं।

str += t.Result;str = str + t.Result;, जहां str से पहले हो रही t.Resultपढ़ा जाता है हो जाता है। इस बिंदु पर, str"123" है। जब FooAsync रनों में निरंतरता, यह str को संशोधित करता है और फिर 5 देता है। तो str अब "1234" है। लेकिन तब str का मूल्य उस FooAsync भाग गया (जो "123" है) में से पहले पढ़ा गया निरंतरता str मूल्य "1235" को सौंपे जाने के 5 साथ concatenated है।

जब आप इसे दो बयान में तोड़ते हैं, int i = t.Result; str += i;, यह व्यवहार नहीं हो सकता है।

+0

दो बयानों में तोड़ने पर व्यवहार अभी भी हो सकता है। यह सिर्फ कम संभावना है। रीड का जवाब देखें। –

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