Automating the InvokeRequired code pattern

后端 未结 9 1196
醉话见心
醉话见心 2020-11-21 23:57

I have become painfully aware of just how often one needs to write the following code pattern in event-driven GUI code, where

private void DoGUISwitch() {
           


        
相关标签:
9条回答
  • 2020-11-22 00:26

    Create a ThreadSafeInvoke.snippet file, and then you can just select the update statements, right click and select 'Surround With...' or 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>
    
    0 讨论(0)
  • 2020-11-22 00:28

    You could write an extension method:

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

    And use it like this:

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

    EDIT: As Simpzon points out in the comments you could also change the signature to:

    public static void InvokeIfRequired<T>(this T c, Action<T> action) 
        where T : Control
    
    0 讨论(0)
  • 2020-11-22 00:35

    You should never be writing code that looks like this:

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

    If you do have code that looks like this then your application is not thread-safe. It means that you have code which is already calling DoGUISwitch() from a different thread. It's too late to be checking to see if it's in a different thread. InvokeRequire must be called BEFORE you make a call to DoGUISwitch. You should not access any method or property from a different thread.

    Reference: Control.InvokeRequired Property where you can read the following:

    In addition to the InvokeRequired property, there are four methods on a control that are thread safe to call: Invoke, BeginInvoke, EndInvoke and CreateGraphics if the handle for the control has already been created.

    In a single CPU architecture there's no problem, but in a multi-CPU architecture you can cause part of the UI thread to be assigned to the processor where the calling code was running...and if that processor is different from where the UI thread was running then when the calling thread ends Windows will think that the UI thread has ended and will kill the application process i.e. your application will exit without error.

    0 讨论(0)
提交回复
热议问题