【问题标题】:Alternative to using Control.Invoke替代使用 Control.Invoke
【发布时间】:2013-07-11 12:08:14
【问题描述】:

在 Visual C# / .Net 应用程序中,我必须读取 SerialPort,因此我为其分配了一个 DataReceivedEventHandler。

但是,我当然不能直接从处理程序更改 UI 控件,因为它们位于单独的线程上。

解决方案似乎是使用 Control.Invoke,但是,我有太多 UI 操作要执行,所以我担心我可能没有走正确的道路。

我应该做以下哪一项?

  • 选项 A:仍然坚持 Invoke,并使用 Invoke 对每个控件执行每个操作。

  • 选项 B:放置一个 50 毫秒的重复计时器,每 50 毫秒检查一次布尔值是否为 DataReceived == true,如果是,则相应地更新 UI 控件。 (每次我在串行端口 DataReceivedEventHandler 中读取数据时,DataReceived 设置为 true,否则为 false。)

  • 选项 C:还有其他选项吗?

更新:

通过以下方式成功(基于@tcarvin 的回答和@Hans Passant 的评论)。

private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
   if (this.InvokeRequired())
   {
      this.BeginInvoke(new EventHandler<SerialDataReceivedEventArgs>(DataReceivedHandler), new object[] { sender, e });
      return;
   }

   tbSerialStatus.Text = "Received text";    
}

【问题讨论】:

  • 尽量缓冲收到的数据。经常调用没有多大意义,你只需要让人类的眼睛开心。无论如何,经常这样做是有问题的,它可能会导致 UI 线程收到太多调用请求并停止绘制的 Firehose 问题。

标签: c# .net multithreading visual-studio-2010 visual-studio


【解决方案1】:

我觉得你想多了。假设您在表单中进行编码,这样的事情应该可以工作。这是从臀部开始的,您可能需要稍微调整一下:

private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{

   if (this.InvokeRequired)
   {
      this.Invoke(new EventHandler<SerialDataReceivedEventArgs>(DataReceivedHandler), sender, e);
      return;
   }

   // everything here runs on the UI thread, do what you like, 
   // and update as many UI controls as you like.

}

如您所见,您不必将每个控件访问包装在单独的 Control.Invoke 中。

【讨论】:

  • 啊,这样就不用单独调用每个控件了!但是为什么是Control.Invoke,不应该是this.Invoke吗?另外,你为什么将DataReceivedHandler 指定为委托——我们已经在那个处理程序中,所以目标不应该是转到 UI 线程上的方法吗?我知道你只是把它写成草稿,但我正在努力确保。
  • 这是一个麻烦的答案。不要像那样使用 Control.BeginInvoke。它是一个属性,而不是一个方法,它总是true。而且从不使用Control.Invoke(),它是死锁的主要来源。特别是在 DataReceived 事件处理程序中,在程序退出时调用 UI 线程上的 SerialPort.Close() 方法有很高的死锁几率。始终使用 BeginInvoke() 代替。
  • 问题已解决 - 谢谢你们! (请参阅我的问题上的工作代码更新。)
  • @HansPassant,你的意思是,不要像那样使用 InvokeRequired 吗?抱歉,这只是凭记忆,我同时使用 VB.NET 和 C#,并且总是在做这样的错字。我的意思是向你展示一次 Control.Invoke(或 BeginInvoke),一旦你在 UI 线程上,你就可以对所有你想要的控件进行操作。
  • @boardbite 值得一提的是,更强大的解决方案会将串行读取累积到一个字节缓冲区中,并在每个事件上进行检查,并且只有在收到完整的消息/帧后,您才会删除从缓冲区中提取并通过 Invoke 或 BeginInvoke 将其发送到 UI。
【解决方案2】:

解决了。这种方法有效:

private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
   if (this.InvokeRequired())
   {
      this.BeginInvoke(new EventHandler<SerialDataReceivedEventArgs>(DataReceivedHandler), new object[] { sender, e });
      return;
   }

   tbSerialStatus.Text = "Received text";    
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    • 2015-09-30
    • 2011-07-04
    • 1970-01-01
    相关资源
    最近更新 更多