C# 处理潜在的非线程安全事件的最佳方法是什么 请考虑以下场景:.NET 2:

C# 处理潜在的非线程安全事件的最佳方法是什么 请考虑以下场景:.NET 2:,c#,.net,C#,.net,我在system.Timers.Timer对象上触发了一个事件。然后,订阅者在收到事件后将一个项目添加到Windows.Forms.Listbox。这将导致跨线程异常 我的问题是,处理这种情况的最佳方式是什么。我提出的解决办法如下: private delegate void messageDel(string text); private void ThreadSafeMsg(string text) { if (this.InvokeRequired) { messa

我在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);
}

这是在.NET2中处理此问题的最佳方法吗?那么.net 3.5呢?

您有一个跨线程异常,因为您试图从UI线程外部访问项目。需要委托才能挂接到消息泵并更改UI

如果您使用表单计时器,那么您将处于UI线程中。但是,如果您使用BackgroundWorkerThread,并且您也需要一个代理,那么您也会遇到同样的问题


请参见

您有一个跨线程异常,因为您试图从UI线程外部访问项目。需要委托才能挂接到消息泵并更改UI

如果您使用表单计时器,那么您将处于UI线程中。但是,如果您使用BackgroundWorkerThread,并且您也需要一个代理,那么您也会遇到同样的问题


请参见

使用Control.invokererequired没有任何意义,您知道它总是如此。已用事件在线程池线程上引发,而不是在UI线程上引发


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

使用Control.InvokeRequired没有任何意义,您知道它总是如此。已用事件在线程池线程上引发,而不是在UI线程上引发


这使得使用System.Timers.Timer没有意义,只需使用System.Windows.Forms.Timer即可。无需使用Control.Begin/Invoke来蒙混过关,当用户关闭窗体时引发事件时,不能使用ObjectDisposedException使程序崩溃。

这与.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...
    }
}
private void ThreadSafeMsg(字符串文本)
{
if(this.invokererequired)
调用(新操作(ThreadSafeMsg),新对象[]{text});
其他的
{
//东西。。。
}
}

这与.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...
    }
}
private void ThreadSafeMsg(字符串文本)
{
if(this.invokererequired)
调用(新操作(ThreadSafeMsg),新对象[]{text});
其他的
{
//东西。。。
}
}

在您的情况下,最简单的解决方案是使用类,但在一般情况下,您可以使用以下解决方案从非GUI线程访问GUI内容(此解决方案适用于.net 2.0,但对于.net 3.5更优雅):

无论从哪个线程、从UI还是从另一个线程,您都可以这样使用它:

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

在您的情况下,最简单的解决方案是使用类,但在一般情况下,您可以使用以下解决方案从非GUI线程访问GUI内容(此解决方案适用于.net 2.0,但对于.net 3.5更优雅):

无论从哪个线程、从UI还是从另一个线程,您都可以这样使用它:

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

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

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

感谢使用表单的指针。计时器…我甚至不知道它存在感谢使用表单的指针。计时器…我甚至不知道它存在