代码之家  ›  专栏  ›  技术社区  ›  fishhead

处理潜在的非线程安全事件的最佳方法是什么

  •  0
  • fishhead  · 技术社区  · 16 年前

    请考虑.NET 2.0的以下方案:

    我有一个在System.Timers.Timer对象上激发的事件。然后,订阅服务器在接收到事件时向windows.forms.listbox添加一个项。这会导致跨线程异常。

    我的问题是什么是处理这种情况的最好方法。我提出的解决方案如下:

    private   delegate void messageDel(string text);
    private   void ThreadSafeMsg(string text)
    {
      if (this.InvokeRequired)
      {
        messageDel d = new messageDel(ThreadSafeMsg);
        this.Invoke(d, new object[] { text });
      }
      else
      {
      listBox1.Items.Add(text);
      listBox1.Update();
      }
    }
    
     // event
    void Instance_Message(string text)
    {
      ThreadSafeMsg(text);
    }
    

    这是在.NET 2中处理此问题的最佳方法吗?.NET 3.5怎么样?

    5 回复  |  直到 16 年前
        1
  •  1
  •   Hans Passant    16 年前

    使用control没有意义。invokeRequired,您 知道 总是这样。在线程池线程上引发Elapsed事件, 从未 UI线程。

    这使得使用System.Timers.Timer变得毫无意义,只需使用System.Windows.Forms.Timer。不需要使用control.begin/invoke,在用户关闭窗体时引发事件时,不能使用objectDisposedException使程序崩溃。

        2
  •  1
  •   user195488    16 年前

    您有一个跨线程异常,因为您试图从UI线程外部访问项。为了钩住消息泵并更改用户界面,学员是必需的。

    如果您使用表单计时器,那么您将进入UI线程。但是,如果使用BackgroundWorkerThread,您也会遇到同样的问题,并且需要在那里有一个委托。

    见 Threading in Windows Forms

        3
  •  1
  •   Kel    16 年前

    在.NET 3.5中几乎是一样的,因为当您从另一个工作线程访问UI线程时,它与Windows窗体和跨线程相关。 但是,您可以使用常规操作<>和func<>,使代码更小,避免手动创建委托。

    像这样:

    private void ThreadSafeMsg(string text)
    {
        if (this.InvokeRequired)
            this.Invoke(new Action<string>(ThreadSafeMsg), new object[] { text });
        else
        {
            // Stuff...
        }
    }
    
        4
  •  0
  •   Sergey Teplyakov    16 年前

    最简单的解决方案是使用 System.Windows.Forms.Timer 类,但一般情况下,您可以使用以下解决方案从非GUI线程访问GUI资料(此解决方案适用于.NET 2.0,但对于.NET 3.5更为优雅):

    public static class ControlExtentions
    {
        public static void InvokeIfNeeded(this Control control, Action doit)
        {
            if (control.InvokeRequired)
                control.Invoke(doit);
            else
                doit();
        }
    }
    

    你可以这样使用它,不管是从哪个线程,从用户界面还是从另一个线程:

    this.InvokeIfNeeded(()=>
       {
        listBox1.Items.Add(text);
        listBox1.Update();
       });
    
        5
  •  0
  •   supercat    16 年前

    根据操作的不同,control.begininvoke可能比control.invoke更好。invoke将等待UI线程处理您的消息,然后再返回。如果UI线程被阻塞,它将永远等待。control.begininvoke将为UI线程排队一条消息,并立即返回。因为如果控件在尝试开始Invoke之前被立即释放,则无法避免异常,因此需要捕获(可能是吞咽)异常(我认为它可能是ObjectDisposedException或IllegalOperationException,具体取决于时间)。当您要发布消息并在消息处理程序中清除或减少消息时,还需要设置标志或计数器(可能使用threading.interlocked.increment/decrement),以确保在阻止UI线程时不会将过多的消息排队。