Cleaning up code littered with InvokeRequired

前端 未结 8 1882
轮回少年
轮回少年 2020-12-04 09:16

I know that when manipulating UI controls from any non-UI thread, you must marshal your calls to the UI thread to avoid issues. The general consensus is that you should use

相关标签:
8条回答
  • 2020-12-04 10:04

    Well how about this:

    public static class ControlHelpers
    {
        public static void InvokeIfRequired<T>(this T control, Action<T> action) where T : ISynchronizeInvoke
        {
            if (control.InvokeRequired)
            {
                control.Invoke(new Action(() => action(control)), null);
            }
            else
            {
                action(control);
            }
        }
    }
    

    Use it like this:

    private void UpdateSummary(string text)
    {
        summary.InvokeIfRequired(s => { s.Text = text });
    }
    
    0 讨论(0)
  • 2020-12-04 10:07

    I've been reading about the arguments back and forth on adding a logic check to find out if the invoke should be used IFF when not on the UI thread and not on the UI thread itself. I wrote a class that examines the time to execute (via Stopwatch) of various methods to get a rough estimate of the efficiency of one method over another.

    The results may be surprising to some of you (these tests were run via the Form.Shown event):

         // notice that we are updating the form's title bar 10,000 times
         // directly on the UI thread
         TimedAction.Go
         (
            "Direct on UI Thread",
            () =>
            {
               for (int i = 0; i < 10000; i++)
               {
                  this.Text = "1234567890";
               }
            }
         );
    
         // notice that we are invoking the update of the title bar
         // (UI thread -> [invoke] -> UI thread)
         TimedAction.Go
         (
            "Invoke on UI Thread",
            () =>
            {
               this.Invoke
               (
                  new Action
                  (
                     () =>
                     {
                        for (int i = 0; i < 10000; i++)
                        {
                           this.Text = "1234567890";
                        }
                     }
                  )
               );
            }
         );
    
         // the following is invoking each UPDATE on the UI thread from the UI thread
         // (10,000 invokes)
         TimedAction.Go
         (
            "Separate Invoke on UI Thread",
            () =>
            {
               for (int i = 0; i < 10000; i++)
               {
                  this.Invoke
                  (
                     new Action
                     (
                        () =>
                        {
                           this.Text = "1234567890";
                        }
                     )
                  );
               }
            }
         );
    

    Results are as follows:

    • TimedAction::Go()+0 - Debug: [DEBUG] Stopwatch [Direct on UI Thread]: 300ms
    • TimedAction::Go()+0 - Debug: [DEBUG] Stopwatch [Invoke on UI Thread]: 299ms
    • TimedAction::Go()+0 - Debug: [DEBUG] Stopwatch [Separate Invoke on UI Thread]: 649ms

    My conclusion is that you can safely invoke at any time, regardless whether you are on the UI thread or a worker thread, without significant overhead of looping back via the message pump. However, performing most of the work ON the UI thread instead of making many calls to the UI thread (via Invoke()) is advantageous and improve efficiency greatly.

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