代码之家  ›  专栏  ›  技术社区  ›  JYelton Melchior Blausand

UI Thread.Invoke()导致句柄泄漏?

  •  7
  • JYelton Melchior Blausand  · 技术社区  · 14 年前

    在什么情况下,当使用委托和代理时,从非UI线程更新UI控件会导致进程的句柄不断增加 .InvokeRequired

    public delegate void DelegateUIUpdate();
    private void UIUpdate()
    {
        if (someControl.InvokeRequired)
        {
            someControl.Invoke(new DelegateUIUpdate(UIUpdate));
            return;
        }
        // do something with someControl
    }
    

    当以循环或计时器间隔调用时,程序的句柄会不断增加。

    编辑:

    如果上述内容被注释掉并修改为:

    public delegate void DelegateUIUpdate();
    private void UIUpdate()
    {
        //if (someControl.InvokeRequired)
        //{
        //   someControl.Invoke(new DelegateUIUpdate(UIUpdate));
        //    return;
        //}
        CheckForIllegalCrossThreadCalls = false;
        // do something with someControl
    }
    

    停止

    编辑2:

    Thread thread;
    private delegate void UpdateGUI();
    bool UpdateTheGui = false;
    
    public Form1()
    {
        InitializeComponent();
    
        thread = new Thread(new ThreadStart(MyThreadLoop));
        thread.Start();
    }
    
    private void MyThreadLoop()
    {
        while (true)
        {
            Thread.Sleep(500);
            if (UpdateTheGui)
            {
                UpdateTheGui = false;
                UpdateTheGuiNow();
            }
        }
    }
    
    private void UpdateTheGuiNow()
    {
        if (label1.InvokeRequired)
        {
            label1.Invoke(new UpdateGUI(UpdateTheGuiNow));
            return;
        }
    
        label1.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
        label2.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
        label3.Text = DateTime.Now.ToString("MM-dd-yyyy HH:mm:ss");
    }
    
    private void btnInvoke_Click(object sender, EventArgs e)
    {
        UpdateTheGui = true;
    }
    
    8 回复  |  直到 14 年前
        1
  •  3
  •   Hans Passant    14 年前

    Invoke()方法不使用任何句柄。但是,这段代码显然是从线程调用的。一根线

    Thread类没有Dispose()方法,尽管它应该有一个。这可能是在设计上,很难可靠地调用线程池线程,这是不可能的。线程需要的5个句柄由终结器释放。如果终结器从未运行,则程序将需要不断增加的句柄数。

    不运行终结器是很不寻常的。你必须有一个程序,启动了很多线程,但没有分配很多内存。这往往只发生在静态测试中。您可以使用Perfmon.exe诊断此情况,使用.NET内存性能计数器并检查是否正在进行gen#0收集。

        2
  •  4
  •   JYelton Melchior Blausand    12 年前

    我也有同样的问题

    this.Invoke(new DelegateClockUpdate(ChangeClock), sender, e);
    

    句柄会增加,因为Invoke是同步的,而且实际上句柄一直处于挂起状态。

    应该使用等待句柄来处理结果,或者使用异步BeginInvoke方法,如下所示。

    this.BeginInvoke(new DelegateClockUpdate(ChangeClock), sender, e);    
    
        3
  •  3
  •   JYelton Melchior Blausand    12 年前

    我在代码里也看到了同样的东西。我通过替换 Invoke BeginInvoke . 把手漏了。

        4
  •  1
  •   JYelton Melchior Blausand    13 年前

    事实上,我看到了和JYelton一样的问题。我在线程中有相同的调用来更新UI。

    一旦排队 someControl.Invoke(new DelegateUIUpdate(UIUpdate)); 调用时,句柄增加1。调用上肯定有某种泄漏,但我不知道是什么原因造成的。这一点已在多个系统上得到验证。

        5
  •  1
  •   vinogradniy    10 年前

    带有显式句柄finalize的Aync调用。示例:

      public static class ActionExtensions
      {
        private static readonly ILog log = LogManager.GetLogger(typeof(ActionExtensions));
    
        /// <summary>
        /// Async exec action.
        /// </summary>
        /// <param name="action">Action.</param>
        public static void AsyncInvokeHandlers(
          this Action action)
        {
          if (action == null)
          {
            return;
          }
    
          foreach (Action handler in action.GetInvocationList())
          {
            // Initiate the asychronous call.  Include an AsyncCallback
            // delegate representing the callback method, and the data
            // needed to call EndInvoke.
            handler.BeginInvoke(
              ar =>
              {
                try
                {
                  // Retrieve the delegate.
                  var handlerToFinalize = (Action)ar.AsyncState;
                  // Call EndInvoke to free resources.
                  handlerToFinalize.EndInvoke(ar);
    
                  var handle = ar.AsyncWaitHandle;
                  if (handle.SafeWaitHandle != null && !handle.SafeWaitHandle.IsInvalid && !handle.SafeWaitHandle.IsClosed)
                  {
                    ((IDisposable)handle).Dispose();
                  }
                }
                catch (Exception exception)
                {
                  log.Error("Async Action exec error.", exception);
                }
              },
              handler);
          }
        }
      }
    

    看到了吗 http://msdn.microsoft.com/en-us/library/system.iasyncresult.asyncwaithandle.aspx 注:

        6
  •  1
  •   C.J. Manca    9 年前

    下面是一个扩展方法,它的功能类似于普通的Invoke调用,但会在以下操作之后清除句柄:

    namespace ExtensionMethods
    {
        public static class ExtensionMethods
        {
            public static void InvokeAndClose(this Control self, MethodInvoker func)
            {
                IAsyncResult result = self.BeginInvoke(func);
                self.EndInvoke(result);
                result.AsyncWaitHandle.Close();
            }
        }
    }
    

    myForm.InvokeAndClose((MethodInvoker)delegate
    {
        someControl.Text = "New Value";
    });
    

    它将阻塞并等待委托执行,然后在返回之前关闭句柄。

        7
  •  0
  •   Justin Ethier    14 年前

    这是使用的标准模式 Invoke 对UI线程进行marshall更新。

    您确定您的问题不是由应用程序中未包含在问题中的其他代码引起的吗?

        8
  •  0
  •   Pavel Radzivilovsky    14 年前