我有这种形式,它生成一个新线程并开始循环监听和等待 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/