Database
 sql >> डेटाबेस >  >> RDS >> Database

.NET . में कार्यक्रम और सूत्र

मैं आपको सीधे तौर पर बताना चाहता हूं कि यह लेख विशेष रूप से थ्रेड्स से संबंधित नहीं है, बल्कि .NET में थ्रेड्स के संदर्भ में घटनाओं से संबंधित है। इसलिए, मैं थ्रेड्स को सही ढंग से व्यवस्थित करने की कोशिश नहीं करूंगा (सभी ब्लॉक, कॉलबैक, रद्द करने, आदि के साथ) इस विषय पर कई लेख हैं।

फ्रेमवर्क संस्करण 4.0 के लिए सभी उदाहरण सी # में लिखे गए हैं (4.6 में, सब कुछ थोड़ा आसान है, लेकिन फिर भी, 4.0 में कई परियोजनाएं हैं)। मैं सी# संस्करण 5.0 से चिपके रहने की भी कोशिश करूंगा।

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

पहली विधि:

 class WrongRaiser
    {
        public event Action<object> MyEvent;
        public event Action MyEvent2;
    }

मैं इस विधि का सावधानीपूर्वक उपयोग करने की सलाह दूंगा। यदि आप इसे सार्वभौमिक नहीं बनाते हैं, तो आप अंततः अपेक्षा से अधिक कोड लिख सकते हैं। इस प्रकार, यदि नीचे दी गई विधियों की तुलना में यह अधिक सटीक संरचना निर्धारित नहीं करेगा।

अपने अनुभव से, मैं कह सकता हूं कि मैंने इसका इस्तेमाल तब किया जब मैंने घटनाओं के साथ काम करना शुरू किया और परिणामस्वरूप खुद को मूर्ख बनाया। अब, मैं इसे कभी नहीं होने दूंगा।

दूसरी विधि:

    class WrongRaiser
    {
        public event MyDelegate MyEvent;
    }

    class MyEventArgs
    {
        public object SomeProperty { get; set; }
    }

    delegate void MyDelegate(object sender, MyEventArgs e);

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

और अब, आइए एक नज़र डालते हैं कि इवेंट के लिए पहले से क्या बनाया गया है।

सार्वभौमिक विधि:

    class Raiser
    {
        public event EventHandler<MyEventArgs> MyEvent;
    }

    class MyEventArgs : EventArgs
    {
        public object SomeProperty { get; set; }
    }

जैसा कि आप देख सकते हैं, यहां हम यूनिवर्सल EventHandler क्लास का उपयोग करते हैं। अर्थात्, अपने स्वयं के हैंडलर को परिभाषित करने की कोई आवश्यकता नहीं है।

आगे के उदाहरणों में सार्वभौमिक विधि है।

आइए इवेंट जेनरेटर के सबसे सरल उदाहरण पर एक नज़र डालें।

    class EventRaiser
    {
        int _counter;

        public event EventHandler<EventRaiserCounterChangedEventArgs> CounterChanged;

        public int Counter
        {
            get
            {
                return _counter;
            }

            set
            {
                if (_counter != value)
                {
                    var old = _counter;
                    _counter = value;
                    OnCounterChanged(old, value);
                }
            }
        }

        public void DoWork()
        {
            new Thread(new ThreadStart(() =>
            {
                for (var i = 0; i < 10; i++)
                    Counter = i;
            })).Start();
        }

        void OnCounterChanged(int oldValue, int newValue)
        {
            if (CounterChanged != null)
                CounterChanged.Invoke(this, new EventRaiserCounterChangedEventArgs(oldValue, newValue));
        }
    }

    class EventRaiserCounterChangedEventArgs : EventArgs
    {
        public int NewValue { get; set; }
        public int OldValue { get; set; }
        public EventRaiserCounterChangedEventArgs(int oldValue, int newValue)
        {
            NewValue = newValue;
            OldValue = oldValue;
        }
    }

यहां हमारे पास काउंटर प्रॉपर्टी वाला एक वर्ग है जिसे 0 से 10 तक बदला जा सकता है। उस पर, काउंटर को बदलने वाले तर्क को एक अलग थ्रेड में संसाधित किया जाता है।

और यहाँ हमारा प्रवेश बिंदु है:

    class Program
    {
        static void Main(string[] args)
        {
            var raiser = new EventRaiser();
            raiser.CounterChanged += Raiser_CounterChanged;
            raiser.DoWork();
            Console.ReadLine();
        }

        static void Raiser_CounterChanged(object sender, EventRaiserCounterChangedEventArgs e)
        {
            Console.WriteLine(string.Format("OldValue: {0}; NewValue: {1}", e.OldValue, e.NewValue));
        }
    }

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

इसके परिणामस्वरूप हमें यही मिलता है:

अब तक सब ठीक है. लेकिन आइए सोचें कि इवेंट हैंडलर को किस थ्रेड में निष्पादित किया जाता है?

मेरे अधिकांश सहयोगियों ने इस प्रश्न का उत्तर "सामान्य तौर पर" दिया। इसका मतलब यह हुआ कि उनमें से किसी को भी यह समझ में नहीं आया कि प्रतिनिधियों की व्यवस्था कैसे की जाती है। मैं इसे समझाने की कोशिश करूंगा।

प्रतिनिधि वर्ग में एक विधि के बारे में जानकारी होती है।

इसके वंशज, MulticastDelegate भी हैं, जिनमें एक से अधिक तत्व हैं।

इसलिए, जब आप किसी ईवेंट की सदस्यता लेते हैं, तो MulticastDelegate वंशज का एक उदाहरण बनाया जाता है। प्रत्येक अगला ग्राहक MulticastDelegate के पहले से बनाए गए इंस्टेंस में एक नई विधि (ईवेंट हैंडलर) जोड़ता है।

जब आप इनवोक विधि को कॉल करते हैं, तो सभी ग्राहकों के हैंडलर को आपके ईवेंट के लिए एक-एक करके बुलाया जाता है। उस पर, जिस थ्रेड में आप इन हैंडलर्स को कॉल करते हैं, वह उस थ्रेड के बारे में कुछ नहीं जानता है जिसमें वे निर्दिष्ट किए गए थे और, तदनुसार, यह उस थ्रेड में कुछ भी सम्मिलित नहीं कर सकता है।

सामान्य तौर पर, उपरोक्त उदाहरण में ईवेंट हैंडलर्स को DoWork () विधि में उत्पन्न थ्रेड में निष्पादित किया जाता है। यही है, घटना पीढ़ी के दौरान, इसे इस तरह से उत्पन्न करने वाला धागा सभी हैंडलर के निष्पादन की प्रतीक्षा कर रहा है। मैं आपको यह बिना आईडी थ्रेड्स को वापस लिए दिखाऊंगा। इसके लिए, मैंने उपरोक्त उदाहरण में कुछ कोड लाइनें बदल दी हैं।

सबूत है कि उपरोक्त उदाहरण में सभी हैंडलर उस थ्रेड में निष्पादित होते हैं जिसे ईवेंट कहा जाता है

विधि जहां घटना उत्पन्न होती है

        void OnCounterChanged(int oldValue, int newValue)
        {
            if (CounterChanged != null)
            {
                CounterChanged.Invoke(this, new EventRaiserCounterChangedEventArgs(oldValue, newValue));
                Console.WriteLine(string.Format("Event Raiser: old = {0}, new = {1}", oldValue, newValue));
            }
                
        }

हैंडलर

        static void Raiser_CounterChanged(object sender, EventRaiserCounterChangedEventArgs e)
        {
            Console.WriteLine(string.Format("OldValue: {0}; NewValue: {1}", e.OldValue, e.NewValue));
            Thread.Sleep(500);
        }

हैंडलर में, हम वर्तमान थ्रेड को आधे सेकंड के लिए सोने के लिए भेजते हैं। यदि हैंडलर मुख्य थ्रेड में काम करते हैं, तो यह समय DoWork() में उत्पन्न थ्रेड के लिए अपना काम पूरा करने और इसके परिणामों को आउटपुट करने के लिए पर्याप्त होगा।

हालाँकि, यहाँ वही है जो हम वास्तव में देखते हैं:

मुझे नहीं पता कि मेरे द्वारा लिखी गई कक्षा द्वारा उत्पन्न घटनाओं को कौन और कैसे संभालना चाहिए, लेकिन मैं वास्तव में नहीं चाहता कि ये हैंडलर मेरी कक्षा के काम को धीमा कर दें। इसलिए, मैं Invoke के बजाय BeginInvoke विधि का उपयोग करूंगा। BeginInvoke एक नया थ्रेड जनरेट करता है।

नोट:दोनों, Invoke और BeginInvoke विधियां प्रतिनिधि या मल्टीकास्टडिलेगेट कक्षाओं के सदस्य नहीं हैं। वे उत्पन्न वर्ग (या ऊपर वर्णित सार्वभौमिक वर्ग) के सदस्य हैं।

अब, यदि हम उस विधि को बदलते हैं जिसमें घटना उत्पन्न होती है, तो हमें निम्नलिखित प्राप्त होंगे:

मल्टी-थ्रेडेड इवेंट जनरेशन:

        void OnCounterChanged(int oldValue, int newValue)
        {
            if (CounterChanged != null)
            {
                var delegates = CounterChanged.GetInvocationList();
                for (var i = 0; i < delegates.Length; i++)
                    ((EventHandler<EventRaiserCounterChangedEventArgs>)delegates[i]).BeginInvoke(this, new EventRaiserCounterChangedEventArgs(oldValue, newValue), null, null);
                Console.WriteLine(string.Format("Event Raiser: old = {0}, new = {1}", oldValue, newValue));
            }
                
        }

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

यदि हम प्रोग्राम चलाते हैं, तो हमें निम्न परिणाम प्राप्त होंगे।

मुझे लगता है, यह बिल्कुल स्पष्ट है कि अब ईवेंट हैंडलर को अलग-अलग थ्रेड्स में निष्पादित किया जाता है, यानी ईवेंट जनरेटर को इस बात की परवाह नहीं है कि कौन, कैसे और कब तक इसकी घटनाओं को संभालेगा।

और यहां सवाल उठता है:अनुक्रमिक प्रबंधन के बारे में क्या? हमारे पास काउंटर है, आखिर। क्या होगा यदि यह राज्यों का क्रमिक परिवर्तन होगा? लेकिन मैं इस प्रश्न का उत्तर नहीं दूंगा, यह इस लेख का विषय नहीं है। मैं केवल इतना कह सकता हूं कि कई तरीके हैं।

एक और बात। एक ही क्रिया को बार-बार न दोहराने के लिए, मैं उनके लिए एक अलग वर्ग बनाने का सुझाव देता हूं।

एसिंक्रोनस इवेंट जेनरेट करने के लिए एक क्लास

    static class AsyncEventsHelper
    {
        public static void RaiseEventAsync<T>(EventHandler<T> h, object sender, T e) where T : EventArgs
        {
            if (h != null)
            {
                var delegates = h.GetInvocationList();
                for (var i = 0; i < delegates.Length; i++)
                    ((EventHandler<T>)delegates[i]).BeginInvoke(sender, e, h.EndInvoke, null);
            }
        }
    }

इस मामले में, हम कॉलबैक का उपयोग करते हैं। इसे हैंडलर के समान थ्रेड में निष्पादित किया जाता है। अर्थात्, हैंडलर विधि पूर्ण होने के बाद, प्रतिनिधि h.EndInvoke अगला कॉल करता है।

यहां बताया गया है कि इसका उपयोग कैसे किया जाना चाहिए

        void OnCounterChanged(int oldValue, int newValue)
        {
            AsyncEventsHelper.RaiseEventAsync(CounterChanged, this, new EventRaiserCounterChangedEventArgs(oldValue, newValue)); 
        }

मुझे लगता है, कि अब यह स्पष्ट हो गया है कि सार्वभौमिक पद्धति की आवश्यकता क्यों थी। यदि हम विधि 2 के साथ घटनाओं का वर्णन करते हैं, तो यह तरकीब काम नहीं करेगी। अन्यथा, आपको अपने प्रतिनिधियों के लिए स्वयं ही सार्वभौमिकता बनानी होगी।

नोट :वास्तविक परियोजनाओं के लिए, मैं थ्रेड के संदर्भ में ईवेंट आर्किटेक्चर को बदलने की अनुशंसा करता हूं। वर्णित उदाहरण थ्रेड के साथ आवेदन के काम को नुकसान पहुंचा सकते हैं, और केवल सूचनात्मक उद्देश्यों के लिए प्रदान किए जाते हैं।

निष्कर्ष

आशा है, मैं यह वर्णन करने में कामयाब रहा कि ईवेंट कैसे काम करते हैं और हैंडलर कहाँ काम करते हैं। अगले लेख में, मैं एसिंक्रोनस कॉल किए जाने पर ईवेंट हैंडलिंग के परिणाम प्राप्त करने के बारे में गहराई से जानने की योजना बना रहा हूं।

मुझे आपकी टिप्पणियों और सुझावों की प्रतीक्षा है।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. हमारा नया SQLPerformance.com न्यूज़लेटर

  2. एमएल{.NET} परिचय

  3. स्केलग्रिड अब सिडनी एडब्ल्यूएस क्षेत्र में उपलब्ध है

  4. लेन-देन लॉग को ट्रिम करना फैट

  5. उन्नत एसक्यूएल:क्रॉस लागू करें और बाहरी आवेदन करें