स्वचालित चालान कोड पैटर्न को स्वचालित करना


179

मुझे दर्द हो रहा है कि बस कितनी बार किसी को इवेंट-संचालित GUI कोड में निम्नलिखित कोड पैटर्न लिखने की आवश्यकता है, जहां

private void DoGUISwitch() {
    // cruisin for a bruisin' through exception city
    object1.Visible = true;
    object2.Visible = false;
}

हो जाता है:

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

यह सी # में एक अजीब पैटर्न है, दोनों को याद रखने के लिए, और टाइप करने के लिए। क्या किसी ने किसी प्रकार का शॉर्टकट बनाया है या निर्माण किया है जो इसे एक हद तक स्वचालित करता है? यह शांत होगा यदि कोई ऑब्जेक्ट को फ़ंक्शन को संलग्न करने का एक तरीका था जो इस object1.InvokeIfNecessary.visible = trueप्रकार के अतिरिक्त काम से गुजरने के बिना यह जांच करता है, जैसे कि एक प्रकार का शॉर्टकट।

पिछले उत्तरों ने हर बार सिर्फ इनवोक () को कॉल करने की अव्यवहारिकता पर चर्चा की है, और फिर भी इनवोक () सिंटैक्स दोनों अक्षम और अभी भी अजीब है।

तो, क्या किसी ने कोई शॉर्टकट निकाला है?


2
मैंने एक ही बात पर आश्चर्य किया है, लेकिन WPF के डिस्पैचर के संबंध में। CheckAccess ()।
टेलर लीज़

मैंने सोचा कि आपकी object1.InvokeIfNecessary.Visible = trueलाइन से प्रेरित एक पागल सुझाव है ; मेरे अपडेट किए गए उत्तर को देखें और मुझे बताएं कि आप क्या सोचते हैं।
डान ताओ

1
मैट डेविस द्वारा सुझाई गई पद्धति को लागू करने में मदद करने के लिए एक स्निपेट जोड़ें: मेरा उत्तर देखें (देर से ही सही लेकिन बाद में आने वाले विशेषज्ञों के लिए कैसे दिखाएं;;)
हारून गैज़

3
मुझे समझ में नहीं आता है कि Microsoft ने .NET में इसे सरल बनाने के लिए कुछ भी क्यों नहीं किया। धागे से फार्म पर प्रत्येक परिवर्तन के लिए प्रतिनिधि बनाना वास्तव में कष्टप्रद है।
कामिल

@ कामिल मैं अधिक सहमत नहीं हो सका! यह एक ऐसा निरीक्षण है, जिसे इसकी सर्वव्यापकता दी गई है। ढांचे के भीतर, यदि आवश्यक हो तो केवल थ्रेडिंग को संभालें। स्पष्ट लगता है।
स्टीविन्क

जवाबों:


138

ली के दृष्टिकोण को और सरल बनाया जा सकता है

public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
    // See Update 2 for edits Mike de Klerk suggests to insert here.

    if (control.InvokeRequired) {
        control.Invoke(action);
    } else {
        action();
    }
}

और इस तरह कहा जा सकता है

richEditControl1.InvokeIfRequired(() =>
{
    // Do anything you want with the control here
    richEditControl1.RtfText = value;
    RtfHelpers.AddMissingStyles(richEditControl1);
});

प्रतिनिधि के लिए पैरामीटर के रूप में नियंत्रण को पारित करने की आवश्यकता नहीं है। C # स्वचालित रूप से एक क्लोजर बनाता है ।


अद्यतन :

कई अन्य पोस्टरों के अनुसार Controlसामान्यीकृत किया जा सकता है ISynchronizeInvoke:

public static void InvokeIfRequired(this ISynchronizeInvoke obj,
                                         MethodInvoker action)
{
    if (obj.InvokeRequired) {
        var args = new object[0];
        obj.Invoke(action, args);
    } else {
        action();
    }
}

DonBoitnott ने बताया कि विपरीत इंटरफेस के लिए एक वस्तु सरणी की आवश्यकता के लिए पैरामीटर सूची के रूप में विधि ।ControlISynchronizeInvokeInvokeaction


अद्यतन २

माइक डी क्लार्क द्वारा सुझाए गए संपादन (सम्मिलित बिंदु के लिए 1 कोड स्निपेट में टिप्पणी देखें):

// When the form, thus the control, isn't visible yet, InvokeRequired  returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
    System.Threading.Thread.Sleep(50);
}

इस सुझाव के बारे में चिंताओं के लिए नीचे टूलमेकरसर्व की टिप्पणी देखें।


2
के ISynchronizeInvokeबजाय के लिए बेहतर होगा Control? (कुडोस जॉन स्कीट को stackoverflow.com/questions/711408/... )
Odys

@odyodyodys: अच्छी बात है। मैं इसके बारे में नहीं जानता था ISynchronizeInvoke। लेकिन एकमात्र प्रकार जो इसे (रिफ्लेक्टर के अनुसार) से प्राप्त होता है Control, इसलिए एडवांटेज सीमित है।
ओलिवियर जैकोट-डेस्कोब्स ने

3
@ माइक-डे-क्लर्क, मुझे आपके सुझाव को जोड़ने की चिंता है while (!control.Visible) ..sleep..। मेरे लिए एक बुरा कोड गंध है, क्योंकि यह एक संभावित अनबाउंड विलंब है (शायद कुछ मामलों में एक अनंत लूप भी), कोड में ऐसे कॉलर्स हो सकते हैं जो इस तरह की देरी (या एक गतिरोध) की उम्मीद नहीं कर रहे हैं। IMHO, किसी भी उपयोग के लिए Sleepप्रत्येक कॉलर की जिम्मेदारी होनी चाहिए, या एक अलग आवरण में होना चाहिए जो स्पष्ट रूप से इसके परिणामों के रूप में चिह्नित है। IMHO, आमतौर पर "फेल हार्ड" (अपवाद, परीक्षण के दौरान पकड़ना) या "कुछ भी नहीं करना" बेहतर होगा यदि नियंत्रण तैयार नहीं है। टिप्पणियाँ?
टूलमेकर

1
@ OlivierJacot-Descombes, यह बहुत अच्छा होगा, अगर आप कृपया बताएं कि थ्रेड.vokerequired कैसे काम करता है?
सुधीर.नेट

1
InvokeRequiredबताता है कि क्या कॉलिंग थ्रेड उस थ्रेड से अलग है जिसने नियंत्रण बनाया है। Invokeकॉलिंग थ्रेड से नियंत्रण के थ्रेड तक एक्शन पास करता है जहां इसे निष्पादित किया जाता है। यह सुनिश्चित करता है कि, उदाहरण के लिए, एक क्लिक ईवेंट हैंडलर कभी बाधित नहीं होता है।
ओलिवियर जैकोट-डेसॉम्ब्स ने

133

आप एक विस्तार विधि लिख सकते हैं:

public static void InvokeIfRequired(this Control c, Action<Control> action)
{
    if(c.InvokeRequired)
    {
        c.Invoke(new Action(() => action(c)));
    }
    else
    {
        action(c);
    }
}

और इसे इस तरह से उपयोग करें:

object1.InvokeIfRequired(c => { c.Visible = true; });

संपादित करें: जैसा कि सिम्प्ज़न टिप्पणियों में बताते हैं कि आप हस्ताक्षर भी बदल सकते हैं:

public static void InvokeIfRequired<T>(this T c, Action<T> action) 
    where T : Control

शायद मैं भी गूंगा हूँ, लेकिन यह कोड संकलित नहीं करेगा। इसलिए मैंने इसे ठीक कर लिया क्योंकि यह मेरे (VS2008) द्वारा बनाया गया था।
ओलिवर

5
बस पूर्णता के लिए: WPF में एक अलग प्रेषण तंत्र है, लेकिन यह अनुरूप है। आप वहां इस एक्सटेंशन विधि का उपयोग कर सकते हैं: सार्वजनिक स्थैतिक शून्य InvokeIfRequired <T> (यह T aTarget, Action <T> actionToExecute) जहां T: DispatcherObject {if (aTarget.CheckAccess ()) {aActionToExecute (aTarget); } और {aTarget.Dispatcher.Invoke (aActionToExecute); }}
साइमन डी।

1
मैंने एक उत्तर जोड़ा जो ली के समाधान को थोड़ा सरल करता है।
ओलिवियर जैकोट-डेसकॉम्बस

नमस्ते, जैसा कि मैं जहां कुछ परिचित का उपयोग कर रहा हूं, इस सामान्य कार्यान्वयन से आने वाली एक बड़ी समस्या हो सकती है। यदि नियंत्रण डिस्पोज़ / डिस्पोज़ किया गया है, तो आपको एक ObjectDisposedException मिलेगी।
ऑफिसर

1
@ ऑफ़लर - यदि वे एक अलग धागे पर निपटाए जा रहे हैं तो आपको एक सिंक्रनाइज़ेशन समस्या है, यह इस पद्धति में कोई समस्या नहीं है।
ली

33

यहाँ मैं अपने सभी कोड का उपयोग कर रहा हूँ।

private void DoGUISwitch()
{ 
    Invoke( ( MethodInvoker ) delegate {
        object1.Visible = true;
        object2.Visible = false;
    });
} 

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

उम्मीद है की यह मदद करेगा।


+1 - मैंने आपके द्वारा किए गए उसी ब्लॉग प्रविष्टि पर ठोकर खाई, और सोचें कि यह किसी प्रस्तावित का सबसे साफ दृष्टिकोण है
टॉम बुशल

3
इस दृष्टिकोण का उपयोग करके एक छोटा प्रदर्शन हिट होता है, जिसे कई बार कॉल करने पर ढेर हो सकता है। stackoverflow.com/a/747218/724944
सर्फ करें

4
InvokeRequiredयदि नियंत्रण दिखाया गया था, तो आपको उपयोग करना होगा यदि कोड निष्पादित किया जा सकता है या आपके पास एक घातक अपवाद होगा।
56ka

9

एक ThreadSafeInvoke.snippet फ़ाइल बनाएं, और फिर आप केवल अपडेट स्टेटमेंट का चयन कर सकते हैं, राइट क्लिक करें और 'सराउंड विथ ...' या Ctrl-K + S चुनें:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>ThreadsafeInvoke</Title>
    <Shortcut></Shortcut>
    <Description>Wraps code in an anonymous method passed to Invoke for Thread safety.</Description>
    <SnippetTypes>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Code Language="CSharp">
      <![CDATA[
      Invoke( (MethodInvoker) delegate
      {
          $selected$
      });      
      ]]>
    </Code>
  </Snippet>
</CodeSnippet>

6

यहां लीज़, ओलिवर और स्टीफ़न के उत्तरों का एक बेहतर / संयुक्त संस्करण दिया गया है।

public delegate void InvokeIfRequiredDelegate<T>(T obj)
    where T : ISynchronizeInvoke;

public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
    where T : ISynchronizeInvoke
{
    if (obj.InvokeRequired)
    {
        obj.Invoke(action, new object[] { obj });
    }
    else
    {
        action(obj);
    }
} 

टेम्पलेट लचीला और कास्ट-कम कोड के लिए अनुमति देता है जो बहुत अधिक पठनीय है जबकि समर्पित प्रतिनिधि दक्षता प्रदान करता है।

progressBar1.InvokeIfRequired(o => 
{
    o.Style = ProgressBarStyle.Marquee;
    o.MarqueeAnimationSpeed = 40;
});

4

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

delegate void ShowMessageCallback(string message);

private void Form1_Load(object sender, EventArgs e)
{
    ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
}

private void ShowMessage(string message)
{
    if (this.InvokeRequired)
        this.Invoke(showMessageDelegate, message);
    else
        labelMessage.Text = message;           
}

void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
{
    ShowMessage(e.Message);
}

3

उपयोग:

control.InvokeIfRequired(c => c.Visible = false);

return control.InvokeIfRequired(c => {
    c.Visible = value

    return c.Visible;
});

कोड:

using System;
using System.ComponentModel;

namespace Extensions
{
    public static class SynchronizeInvokeExtensions
    {
        public static void InvokeIfRequired<T>(this T obj, Action<T> action)
            where T : ISynchronizeInvoke
        {
            if (obj.InvokeRequired)
            {
                obj.Invoke(action, new object[] { obj });
            }
            else
            {
                action(obj);
            }
        }

        public static TOut InvokeIfRequired<TIn, TOut>(this TIn obj, Func<TIn, TOut> func) 
            where TIn : ISynchronizeInvoke
        {
            return obj.InvokeRequired
                ? (TOut)obj.Invoke(func, new object[] { obj })
                : func(obj);
        }
    }
}

2

मुझे यह पसंद है कि मैं इसे थोड़ा अलग करूं, मुझे एक्शन के साथ ज़रूरत पड़ने पर "खुद को" कॉल करना पसंद है,

    private void AddRowToListView(ScannerRow row, bool suspend)
    {
        if (IsFormClosing)
            return;

        if (this.InvokeRequired)
        {
            var A = new Action(() => AddRowToListView(row, suspend));
            this.Invoke(A);
            return;
        }
         //as of here the Code is thread-safe

यह एक आसान पैटर्न है, IsFormClosing एक ऐसा क्षेत्र है जिसे मैं ट्रू पर सेट करता हूं जब मैं अपना फॉर्म बंद कर रहा हूं क्योंकि कुछ पृष्ठभूमि धागे हो सकते हैं जो अभी भी चल रहे हैं ...


-3

आपको कभी भी ऐसा कोड नहीं लिखना चाहिए जो इस तरह दिखता हो:

private void DoGUISwitch() {
    if (object1.InvokeRequired) {
        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
    } else {
        object1.Visible = true;
        object2.Visible = false;
    }
}

यदि आपके पास ऐसा कोड है जो इस तरह दिखता है तो आपका एप्लिकेशन थ्रेड-सुरक्षित नहीं है। इसका मतलब है कि आपके पास कोड है जो पहले से ही एक अलग धागे से DoGUISwitch () कह रहा है। यह देखने के लिए बहुत देर हो चुकी है कि क्या यह अलग थ्रेड में है या नहीं। DoGUISwitch पर कॉल करने के लिए InvokeRequire को पहले से ही बुलाया जाना चाहिए। आपको किसी अलग थ्रेड से किसी भी विधि या संपत्ति तक नहीं पहुंचना चाहिए।

संदर्भ: नियंत्रण। InvokeRequired संपत्ति जहां आप निम्नलिखित पढ़ सकते हैं:

InvokeRequired प्रॉपर्टी के अलावा, एक कंट्रोल पर चार तरीके हैं जो कॉल करने के लिए थ्रेड सुरक्षित हैं: इनवोक, बिगिन इनवाइस, एंडइनवोक और क्रिएटग्राफिक्स यदि कंट्रोल के लिए हैंडल पहले ही बनाया जा चुका है।

एक सिंगल सीपीयू आर्किटेक्चर में कोई समस्या नहीं है, लेकिन मल्टी-सीपीयू आर्किटेक्चर में आप यूआई थ्रेड के हिस्से को प्रोसेसर को सौंपा जा सकता है, जहां कॉलिंग कोड चल रहा था ... और अगर वह प्रोसेसर यूआई थ्रेड से अलग है तब चल रहा था जब कॉलिंग थ्रेड समाप्त होता है विंडोज सोचता है कि यूआई थ्रेड समाप्त हो गया है और आवेदन प्रक्रिया को मार देगा अर्थात आपका एप्लिकेशन त्रुटि के बिना बाहर निकल जाएगा।


अरे वहाँ, आपके उत्तर के लिए धन्यवाद। मुझे यह सवाल पूछे हुए कई साल हो चुके हैं (और लगभग इतना ही समय जब से मैंने C # के साथ काम किया है), लेकिन मैं सोच रहा था कि क्या आप थोड़ा और समझा सकते हैं? invoke()नियंत्रण से पहले एट अल कॉलिंग के एक विशिष्ट खतरे को संदर्भित करने के लिए आपके द्वारा लिंक किए गए डॉक्स को संभाल दिया गया है, लेकिन IMHO ने आपके द्वारा वर्णित विवरण का वर्णन नहीं किया है। इस सभी invoke()बकवास का पूरा बिंदु यूआई को थ्रेड-सुरक्षित तरीके से अपडेट करना है, और मुझे लगता है कि एक अवरुद्ध संदर्भ में अधिक निर्देश डालने से हकलाना होगा? (उह ... खुशी है कि मैंने एम $ टेक का उपयोग करना बंद कर दिया। इसलिए जटिल!)
टॉम कोरेलिस

मैं यह भी नोट करना चाहता हूं कि मूल कोड (जब वापस)) के लगातार उपयोग के बावजूद, आपने मेरे दोहरे-सीपीयू डेस्कटॉप पर वर्णित समस्या का अवलोकन नहीं किया
टॉम कोरेलिस

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