如何妥善保管,同时在C#中传输的数据包更新UI?(How to properly keep the

2019-07-28 20:56发布

我有这样的形式产生一个新的线程,并开始监听,等待在一个循环的UDP数据包。 我需要的是保持与接收的字节数更新的用户界面。

对于这一点,我已经安装其作为收到一个包时我会尽快提高,并通过接受作为参数的字节数的事件。 因为我不是在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减慢数据包接收。 如果我走这条线断(或评论它),数据包接收会快很多。

我怎么可能解决这个问题呢? 我想更多的线程是关键,但我不知道如何正确地实现他们在这种情况下...我使用Windows窗体和.NET 2.0。

编辑:

在我以前的测试,上面似乎是真实的,它实际上可能是在一定程度上。 但有点更多的测试后,我意识到这个问题是在整个Invoke(new MethodInvoker(() => { ... })); 事情。 当我删除(用户界面将当然不是更新),并留下EVENTHANDLER_UpdateTransferProgress但不断提高的情况下,数据包接收的速度要快得多。

我测试得到了一些文件,它把平均约为〜1.5秒,而无需调用Invoke()在所有的事件处理程序。 当我做调用Invoke()事件处理程序,即使没有更新的用户界面的任何控制或做任何操作(换句话说,匿名方法体是空的),花了更长的时间,围绕〜5.5sec。 你可以看到这是一个很大的区别。

反正是有提高吗?

Answer 1:

你的方法的问题是,它更新每一个数据包的用户界面。 如果您收到1000个数据包每秒,你会更新UI每秒1000次! 监视器可能不会刷新每秒超过100次,没有人会能够读取它,如果它每秒更新10次以上。

一个更好的办法来解决这个问题是把totalReceivedBytes += receivedBytes; 在处理所述I / O,并把一个计时器执行在UI线程上的螺纹Label.Text = totalReceivedBytes.ToString("##,0"); 只有每秒几次最多。 当传输开始,启动定时器; 停止传输时,停止计时。



Answer 2:

是的,有改善此问题的方法。

首先是使用BeginInvoke ,而不是Invoke它不会等待调用返回。 你也应该考虑在你的方法用另一种形式

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

所以,如果你从不需要调用一个方法这个方法,仍然执行GUI上的更新。


你可以做的另一种方法是在你的下载线程丝的断裂。 在东西的喜欢

public event EventHandler<MonitorEventArgs> ReportProgress;

public void startSendingUpdates(MonitorEventArgs args) {
  EventHandler<MonitorEventArgs> handler = ReportProgress;
  if (handler == null) {
      return;
  }
  ThreadPool.QueueUserWorkItem(delegate {
      while (!args.Complete) {
          handler(this, args);
          Thread.Sleep(800);
      }
  });
}

public void download() {
    MonitorEventArgs args = new MonitorEventArgs();
    startSendingUpdates(args);
    while (downloading) {
        int read = downloadData(bytes);
        args.BytesTransferred += read;
    }
    args.Complete = true;
}

public class MonitorEventArgs : EventArgs {
    public bool Complete { get; set; }
    public long BytesTransferred { get; set; }
}

相比于利益这样做的开销是那种小。 您的下载线程不会受更新的GUI(至少不会比等待的GUI更新)。 缺点是你占据一个线程的线程池,但是,嘿,这就是他们有什么! 而且,线程关闭当它这样做,因为你设置的完成标志。 你不需要设置,要么当,因为在工作线程额外的运行是在上下文不重要锁定。



Answer 3:

你是否尝试过使用,而不是调用的BeginInvoke? 的BeginInvoke()是一个台异步调用。

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


文章来源: How to properly keep the UI updated while transferring packets in C#?