跨线程UI组件调用

3

这种处理跨线程操作的方式是否合适?

我应该使用一个新的属性名称,比如"EditValueThreadSafe"而不是覆盖"EditValue"吗?我认为对于EditValue的实现更改没有问题,因为基本属性仍然被调用。

namespace MyApplication.Components
{
    using System.Windows.Forms;

    /// <summary>
    /// Thread-safe implementation of the DevExpress.XtraEditors.ComboBoxEdit class.
    /// </summary>
    public class ComboBoxEditThreadSafe : DevExpress.XtraEditors.ComboBoxEdit
    {
        /// <summary>
        /// Gets or sets the edit value.
        /// </summary>
        /// <value>The edit value.</value>
        public override object EditValue
        {
            get
            {
                return base.EditValue;
            }

            set
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(new MethodInvoker(delegate
                    {
                        this.SetEditValue(value);
                    }));
                }
                else
                {
                    this.SetEditValue(value);
                }
            }
        }

        /// <summary>
        /// Sets the edit value.
        /// </summary>
        /// <param name="value">The value.</param>
        private void SetEditValue(object value)
        {
            base.EditValue = value;
        }
    }
}
4个回答

2

您也可以委托给另一个执行工作的方法,在该方法中,如果在错误的线程上(BeginInvoke返回true),则再次调用相同的方法。这样做可以消除重复代码的需要。

public class ComboBoxEditThreadSafe : DevExpress.XtraEditors.ComboBoxEdit    
{       
   public override object EditValue        
   {            
       get            
       {                             
            return base.EditValue;            
       }            
       set  
       {                
         SetValue(value);
       }
   }



    private void delegate SetValueDlg(object valeu);
    private void SetValue(object value)
    {
        if (this.InvokeRequired)
             this.BeginInvoke(
                 (SetValueDlg)SetValue,  // calls itself, but on correct thread
                 new object[] { value });
        else

              base.editValue = value;  

    }
}

你也可以使用Action()泛型类来消除创建显式委托类的需要...
   public class ComboBoxEditThreadSafe : DevExpress.XtraEditors.ComboBoxEdit    
{       
   public override object EditValue        
   {            
       get {  return base.EditValue;   }            
       set { SetValue(value); }
   }

   private void SetValue(object value)
   {
       if (this.InvokeRequired)
           this.BeginInvoke(
               new Action<object>(SetValue),  // calls itself, but on correct thread
               new object[] { value });
       else                
              base.editValue = value;  

   }

}


我不明白你的意思。 - Armbrat
那看起来是一个不错的方法。 - Armbrat

1

它是线程安全的,但要小心覆盖属性并从根本上改变行为。更改实现很好,但此属性现在的行为已经非常不同,去除了特定异常的可能性,但引入了可能的死锁或阻塞条件,这会影响调用代码。

所以是的,这是InvokeRequired和Invoke的正确使用方式,但我建议创建一个单独的、特定目的和线程安全的属性,并将其公开。


0

我的UI方法和你的一样,最终看起来像这样:

public void setStatusLabelText(String s)
    {
        if (footerStatusLabel.InvokeRequired) {
            StringUpdateInvokeDelegate callback = new StringUpdateInvokeDelegate(setStatusLabelText);
            this.Invoke(callback, new object[] { s });
        }
        else {
            this.footerStatusLabel.Text = s;
        }            
    }

(这在现今的 .net 可能已经过时了 - 但重点是,如果您已经在正确的线程上,您可以在此方法内执行操作 - 这使得阅读略微不那么烦人,但与 Java 相比仍然很烦人,我个人认为)。


1
我已经厌倦了写很多的调用,当我意识到我可以在派生类中完成它时。 - Armbrat

0

我来发表一下我的看法。实际调用InvokeRequired/BeginInvoke/Invoke并不完全是线程安全的。(参见避免跨线程WinForm事件处理中Invoke/BeginInvoke的问题?)我建议找到某种方式将这些调用隔离在一个单独的地方,例如实用程序API、扩展方法或类似的东西。在上面的文章中,有一个完整的代码示例,展示了如何包装委托以提供线程安全行为。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接