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

时间:2010-08-20 16:08:41

标签: c# .net

请考虑.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 个答案:

答案 0 :(得分:1)

您有一个跨线程异常,因为您尝试从UI线程外部访问项目。代表是必要的,以便挂钩消息泵并进行UI更改。

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

请参阅Threading in Windows Forms

答案 1 :(得分:1)

使用Control.InvokeRequired是没有意义的,你知道它始终是。 Elapsed事件是在线程池线程上引发的,从不 UI线程。

这使得使用System.Timers.Timer变得毫无意义,只需使用System.Windows.Forms.Timer即可。无需使用Control.Begin / Invoke,当用户关闭表单时引发事件时,不能使用ObjectDisposedException使程序崩溃。

答案 2 :(得分:1)

在.net 3.5中几乎相同,因为当您从另一个工作线程访问UI线程时,它与Windows窗体和跨线程相关。 但是,您可以通过使用通用Action<>来缩小代码。和Func<&gt ;,避免手动创建代理。

这样的事情:

private void ThreadSafeMsg(string text)
{
    if (this.InvokeRequired)
        this.Invoke(new Action<string>(ThreadSafeMsg), new object[] { text });
    else
    {
        // Stuff...
    }
}

答案 3 :(得分:0)

您的案例中最简单的解决方案 - 使用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();
    }
}

你可以像这样使用它来自什么线程,来自UI或来自另一个:

this.InvokeIfNeeded(()=>
   {
    listBox1.Items.Add(text);
    listBox1.Update();
   });

答案 4 :(得分:0)

根据您的操作正在做什么,Control.BeginInvoke可能比Control.Invoke更好。 Control.Invoke将等待UI线程在返回之前处理您的消息。如果UI线程被阻止,它将永远等待。 Control.BeginInvoke将为UI线程排队消息并立即返回。因为如果在尝试BeginInvoke它之前立即处理控件,就无法避免异常,您需要捕获(可能吞下)异常(我认为它可能是ObjectDisposedException或IllegalOperationException,具体取决于时间)。当您即将发布消息并在消息处理程序中清除或减少消息时,您还需要设置一个标志或计数器(可能使用Threading.Interlocked.Increment / Decrement),以确保您不会排入过多的数字UI线程被阻止时的消息。

相关问题