c# - 在 C# 中传输数据包时如何正确保持 UI 更新?

标签 c# multithreading sockets packet-capture ui-thread

我有这种形式,它生成一个新线程并开始循环监听和等待 UDP 数据包。我需要的是根据接收到的字节数来更新 UI。

为此,我设置了一个事件,一旦收到数据包,我就会引发该事件,并将收到的字节数作为参数传递。由于我不是在 UI 线程上运行,所以我不能简单地直接更新 UI。这是我目前正在做的事情:

private void EVENTHANDLER_UpdateTransferProgress(long receivedBytes) {
    if(InvokeRequired) {
        Invoke(new MethodInvoker(() => {
            totalReceivedBytes += receivedBytes;
            Label.Text = totalReceivedBytes.ToString("##,0");
        }));
    }
}

但这仍然在与数据包接收循环相同的线程上运行,并且不会返回到该循环 - 并等待另一个数据包 - 直到这个 EVENTHANDLER_UpdateTransferProgress 方法返回。

我的问题基本上是关于上述方法中的以下行:

Label.Text = totalReceivedBytes.ToString("##,0");

像这样更新 UI 会减慢数据包接收速度。如果我取消该行(或对其进行注释),数据包接收将会快得多。

我怎样才能解决这个问题?我认为更多线程是关键,但我不确定在这种情况下如何正确实现它们...我正在使用带有 .NET 2.0 的 Windows 窗体。

编辑:

根据我之前的测试,上述内容似乎是正确的,而且实际上在某种程度上可能是正确的。但经过更多测试后,我意识到问题出在整个 Invoke(new MethodInvoker(() => { ... })); 事情上。当我删除它(UI 当然不会更新)并保留 EVENTHANDLER_UpdateTransferProgress 但继续引发事件时,数据包接收速度要快得多。

我测试了接收一些文件,该文件平均需要约 1.5 秒,而无需在事件处理程序上调用 Invoke() 。当我在事件处理程序中调用 Invoke() 时,即使没有更新 UI 中的任何控件或执行任何操作(换句话说,匿名方法主体为空),也花费了更长的时间,大约〜5.5秒。您可以看到差异很大。

有什么办法可以改进吗?

最佳答案

您的方法的问题在于它会更新每个数据包上的 UI。如果每秒收到 1000 个数据包,则每秒将更新 UI 1000 次!监视器每秒刷新可能不会超过 100 次,如果每秒更新超过 10 次,没有人能够读取它。

解决此问题的更好方法是将 totalReceivedBytes += receiveBytes; 放在处理 I/O 的线程中,并在执行 Label 的 UI 线程上放置一个计时器。 Text = TotalReceivedBytes.ToString("##,0"); 每秒最多只能执行几次。当传输开始时,启动定时器;当传输停止时,停止计时器。

关于c# - 在 C# 中传输数据包时如何正确保持 UI 更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10066884/

相关文章:

c# - Outlook 电子邮件是否有非二进制格式?

c# - 如何将方法标记为过时或已弃用?

java - 多线程程序中的数据不一致

java - 将现有的 java 应用程序与 tomcat & cie 一起使用

c# - 使用嵌入式(托管)IronPython 为 C# 应用程序分发 DLR 运行时

c# - 使用构造的泛型类型中的 'ConstructorInfo',如何从开放类型中获取匹配的 'ConstructorInfo'?

c# - 如何轻松地将两个异步请求链接在一起?

java - 多线程 Java 应用程序中的 ConcurrentModificationException

C Socket Server 无法终止连接?

c# - 在哪里可以找到 SocketException 抛出的 SocketErrorCode 和 NativeErrorCode 列表?