【问题标题】:Speeding up realtime Gui updates from a thread从线程加速实时 Gui 更新
【发布时间】:2011-04-24 23:46:04
【问题描述】:

这是我多年来用来接收网络数据并在我的 GUI(对话框、表单等)中使用的一种技术。

    public delegate void mydelegate(Byte[] message);

    public ReceiveEngineCS(String LocalIpIn, String ReceiveFromIp, mydelegate d)
    {
         this.m_LocalIpIn = LocalIpIn;
         this.m_ReceiveFromIp = ReceiveFromIp;
         m_MainCallback = d;
         SetupReceive();
         m_Running = true;
         //Create the Track receive thread and pass the parent (this)
         m_RtdirReceiveThread = new Thread(new ParameterizedThreadStart(MessageRecieveThread));
         m_RtdirReceiveThread.Start(this);
    }

    private void MessageRecieveThread(System.Object obj)
    {
         ReceiveEngineCS parent = (ReceiveEngineCS)obj;

         while(parent.m_Running)
         {
              Byte[] receiveBytes = new Byte[1500];
              try
              {
                   receiveBytes = parent.m_ClientReceiver.Receive(ref parent.ipEndPoint);
                   parent.ThreadOutput(receiveBytes);   
              }
              catch ( Exception e )
              {
                  parent.StatusUpdate(e.ToString()); 
              }                         
         }          
    }

    public void ThreadOutput(Byte[] message)
    {
         m_MainCallback(message);           
    }

public partial class SystemMain : Form
{
    //Local Class Variables
    Network.ReceiveEngineCS SystemMessageReceiver;
    private void Form1_Load(object sender, EventArgs e)
    {
        //Load up the message receiver
        SystemMessageReceiver = new Network.ReceiveEngineCS(localAddy, fromAddy, new mydelegate(LocalDelegate));
    }

    public void LocalDelegate(Byte[] message)
    {
        if (Form.ListView.InvokeRequired)
        {
            //External call: invoke delegate
            Form.ListView.Invoke(new mydelegate(this.LocalDelegate), message);
        }
        else
        {
            //Get the Packet Header
            Formats.PacketHeaderObject ph = new Formats.PacketHeaderObject(message);
            //Update or Add item to Specific ListView
            ... update views
        }
    }
 }

接收器每秒接收 10 到 100 条实时消息,通常更多。

我最近一直在研究 .Net 4.0 和 C#,并注意到许多其他类似的方法来进行这种数据处理 ,比如 Worker Threads,以及其他使用 Delegate 和 Invoke 的方式。

我的问题...在较新的 .Net 库(3.5、4.0 等)中是否有更有效的方法来接收数据/更新 GUI?

我认为这种方法不适用于 C#。

任何帮助将不胜感激。

【问题讨论】:

  • 每秒向用户发送一百个通知没有多大意义。它看起来像一个模糊。首先让您的 UI 可用,也可以解决您的问题。 Winforms 没有变化。
  • 好主意汉斯。如果我在线程中建立有关传入数据的元数据并且仅每秒左右更新一次 gui 它仍然会给用户提供关于数据的体面响应,同时允许 Gui 不会陷入更新的困境。通常,运营商使用更新数据项的粗略计数作为数据速率的指导。因此,一目了然地以每秒 10 次 50 次等计算。但我可以将其合并为元更新并将其添加到列表视图中。

标签: c# multithreading networking delegates invoke


【解决方案1】:

将更新发布到 GUI 的最佳方法之一是让工作线程打包更新中包含的数据并将其放入队列中。然后 UI 线程将定期轮询队列。在我看来,让工作线程使用 Control.Invoke 更新 UI 是方式过度使用。相比之下,让 UI 线程轮询更新有几个优点。

  • 它打破了Control.Invoke 强加的 UI 和工作线程之间的紧密耦合。
  • 它将更新 UI 线程的责任放在它本来应该属于的 UI 线程上。
  • UI 线程可以决定更新的时间和频率。
  • 不存在 UI 消息泵溢出的风险,就像工作线程启动的封送技术一样。
  • 工作线程无需等待确认更新已执行,即可继续执行后续步骤(即,您可以在 UI 和工作线程上获得更高的吞吐量)。

您没有提到ThreadOutput 是如何实现的,但是如果还没有这样做的话,您可以考虑我上面提到的方法。经验告诉我,这通常是最好的方法。让 UI 线程限制其更新周期是一个很大的优势。

【讨论】:

  • 我一直遇到的从共享资源轮询数据的问题是时间问题。锁定和解锁共享队列会导致大量开销(当然不是说我现在没有这个问题)。我查看了工作线程,发现线程有一种机制可以发回更新。我不确定是否必须使用工作线程调用来更新 UI 拥有的队列?
  • Control.Invoke 相比,队列的锁定是一项极其昂贵的操作。另外,您始终可以使用使用无锁机制的ConcurrentQueue。工作线程不必使用Invoke 将某些内容发布到队列中。首先将其称为“UI 拥有的队列”可能有点误导。
  • 这有助于@Brian。我不确定调用的费用。我一直在从简单文件或低速率 xml 数据更新 gui 的简单示例中看到它。我将查看 ConcurrentQueue 并考虑按照@Hans 的建议减少需要更新的内容。
  • 我想进一步了解这是如何实现的。您是否在工作线程将其消息插入的 UI 线程上创建共享队列(数组/字典)?队列是否必须保存对要更新的每个 ui 控件的引用?有什么链接可以指向我们吗?
【解决方案2】:

查看Thread Pooling,而不是每次都旋转新线程。

【讨论】:

  • 感谢 Zamboni,但实际上我只使用一个线程来接收应用程序的整个生命周期的消息。这些消息是我需要随时接收的事件的持续更新。
猜你喜欢
  • 1970-01-01
  • 2018-10-10
  • 1970-01-01
  • 2023-04-02
  • 2010-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-26
相关资源
最近更新 更多