2011-11-30 14 views
14

निष्पादित करता है जबकि मेरा यूआई कोड (सी # .NET 4.0 एप्लिकेशन में) अपडेट करते समय, गलत गलती में यूआई को निष्पादित करने के लिए कॉल के कारण मैं एक अजीब दुर्घटना में भाग गया। हालांकि, मैं पहले से ही मुख्य धागे पर कॉल कर रहा था, इसलिए दुर्घटना का कोई मतलब नहीं था: MainThreadDispatcher.Invoke(new Action(View.Method)) "कॉलिंग थ्रेड इस ऑब्जेक्ट तक नहीं पहुंच सकता क्योंकि एक अलग थ्रेड इसका मालिक है।" संपत्ति देखें पर।सी # विधि समूह का उपयोग कोड

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

यह कम से कम कहने के लिए दिलचस्प लगता है। क्या सी # मानक में कोई जगह है जहां इसका उल्लेख किया गया है? या सही रूपांतरण खोजने की आवश्यकता के कारण यह अंतर्निहित है?

यहां एक परीक्षण कार्यक्रम है। सबसे पहले, सीधा तरीका। दूसरा, दो चरणों में, जो बेहतर दिखाता है कि क्या होता है। अतिरिक्त मज़े के लिए, मैं प्रतिनिधि बनने के बाद Item संशोधित करता हूं।

namespace ConsoleApplication1 // Add a reference to WindowsBase to a standard ConsoleApplication 
{ 
    using System.Threading; 
    using System.Windows.Threading; 
    using System; 

    static class Program 
    { 
     static Dispatcher mainDispatcher; 
     static void Main() 
     { 
      mainDispatcher = Dispatcher.CurrentDispatcher; 
      mainDispatcher.Thread.Name = "Main thread"; 
      var childThread = new Thread(() => 
       { 
        Console.WriteLine("--- Method group ---"); 
        mainDispatcher.Invoke(new Action(Item.DoSomething)); 

        Console.WriteLine("\n--- Lambda ---"); 
        mainDispatcher.Invoke(new Action(() => Item.DoSomething())); 

        Console.WriteLine("\n--- Method group (two steps) ---"); 
        var action = new Action(Item.DoSomething); 
        Console.WriteLine("Invoking"); 
        mainDispatcher.Invoke(action); 

        Console.WriteLine("\n--- Lambda (two steps) ---"); 
        action = new Action(() => Item.DoSomething()); 
        Console.WriteLine("Invoking"); 
        mainDispatcher.Invoke(action); 

        Console.WriteLine("\n--- Method group (modifying Item) ---"); 
        action = new Action(Item.DoSomething); 
        item = null; 
        mainDispatcher.Invoke(action); 
        item = new UIItem(); 

        Console.WriteLine("\n--- Lambda (modifying Item) ---"); 
        action = new Action(() => Item.DoSomething()); 
        item = null; 
        Console.WriteLine("Invoking"); 
        mainDispatcher.Invoke(action); 

        mainDispatcher.InvokeShutdown(); 
       }); 
      childThread.Name = "Child thread"; 
      childThread.Start(); 

      Dispatcher.Run(); 
     } 

     static UIItem item = new UIItem(); 
     static UIItem Item 
     { 
      get 
      { 
       // mainDispatcher.VerifyAccess(); // Uncomment for crash. 
       Console.WriteLine("UIItem: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name); 
       return item; 
      } 
     } 

     private class UIItem 
     { 
      public void DoSomething() 
      { 
       Console.WriteLine("DoSomething: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name); 
      } 
     } 
    } 
} 

लघु संस्करण:

namespace ConsoleApplication1 // Add a reference to WindowsBase to a standard ConsoleApplication 
{ 
    using System.Threading; 
    using System.Windows.Threading; 
    using System; 

    static class Program 
    { 
     static Dispatcher mainDispatcher; 
     static void Main() 
     { 
      mainDispatcher = Dispatcher.CurrentDispatcher; 
      mainDispatcher.Thread.Name = "Main thread"; 
      var childThread = new Thread(() => 
       { 
        Console.WriteLine("--- Method group ---"); 
        mainDispatcher.Invoke(new Action(Item.DoSomething)); 

        Console.WriteLine("\n--- Lambda ---"); 
        mainDispatcher.Invoke(new Action(() => Item.DoSomething()));  

        mainDispatcher.InvokeShutdown(); 
       }); 
      childThread.Name = "Child thread"; 
      childThread.Start(); 

      Dispatcher.Run(); 
     } 

     static UIItem item = new UIItem(); 
     static UIItem Item 
     { 
      get 
      { 
       mainDispatcher.VerifyAccess(); 
       Console.WriteLine("UIItem: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name); 
       return item; 
      } 
     } 

     private class UIItem 
     { 
      public void DoSomething() 
      { 
       Console.WriteLine("DoSomething: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name); 
      } 
     } 
    } 
} 
+2

कौन सी कॉल विफल हो जाती है? कॉल स्टैक क्या है? – SLaks

+0

VerifyAccess() के साथ टिप्पणी की गई पंक्ति को हटाएं, और आप देखेंगे कि विधि समूह का उपयोग करने वाली सभी कॉल विफल हो जाती हैं, क्योंकि आइटम प्रॉपर्टी को बाल थ्रेड पर एक्सेस किया जाता है। –

+1

एक छोटा लेकिन पूरा कार्यक्रम * बस * समस्याग्रस्त कॉल दिखा रहा है वास्तव में सहायक होगा। –

उत्तर

4

तथ्य यह है कि संपत्ति बेसब्री से पहुँचा जा जाएगा किसी भी तरह से विधि-समूह के सदस्यों को विशेष नहीं है; यह सामान्य रूप से सदस्य-अभिव्यक्तियों की एक विशेषता है।

यह वास्तव में लैम्बडा है जो विशेष मामला बना रहा है: इसके शरीर (और इस प्रकार संपत्ति-पहुंच) को तब तक स्थगित कर दिया जाएगा जब तक कि प्रतिनिधि वास्तव में निष्पादित नहीं हो जाता।

विनिर्देश से

:

7.6.4 सदस्य पहुँच

[...] एक सदस्य का उपयोग प्रपत्र के दोनों ईआई या है प्रपत्र ईआई, जहां ई एक है की प्राथमिक अभिव्यक्ति।

[...] अगर ई एक संपत्ति या इंडेक्सर का उपयोग, तो मूल्य संपत्ति या इंडेक्सर उपयोग की (§7.1.1) और ई प्राप्त किया जाता है एक मूल्य के रूप में पुनर्वर्गीकृत किया जाता है।

6

आप एक closed delegate, जो प्रतिनिधि के अंदर this वस्तु संग्रहीत करता है बना रहे हैं। (विधि के छिपे पहले पैरामीटर के रूप में पास करने के लिए।)

इसलिए, जब आप किसी विधि समूह से प्रतिनिधि बनाते हैं, तो ऑब्जेक्ट को तुरंत प्रतिनिधि में स्टोर करने के लिए एक्सेस किया जाता है।

इसके विपरीत, जब आप लैम्ब्डा अभिव्यक्ति बनाते हैं, तो प्रतिनिधि को स्वामित्व वाली वस्तु केवल तब तक पहुंच जाती है जब प्रतिनिधि को बुलाया जाता है।
आपका लैम्ब्डा अभिव्यक्ति एक खुला प्रतिनिधि बनाता है जो सीधे प्रतिनिधि के भीतर static संपत्ति का उपयोग करता है।

यदि यह एक गैर स्थैतिक संपत्ति या स्थानीय चर का उपयोग करता, तो यह एक बंद प्रतिनिधि from a closure बनाया होता, और यह अभी भी काम करेगा।

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