我有这样的形式产生一个新的线程,并开始监听,等待在一个循环的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。 你可以看到这是一个很大的区别。
反正是有提高吗?
你的方法的问题是,它更新每一个数据包的用户界面。 如果您收到1000个数据包每秒,你会更新UI每秒1000次! 监视器可能不会刷新每秒超过100次,没有人会能够读取它,如果它每秒更新10次以上。
一个更好的办法来解决这个问题是把totalReceivedBytes += receivedBytes;
在处理所述I / O,并把一个计时器执行在UI线程上的螺纹Label.Text = totalReceivedBytes.ToString("##,0");
只有每秒几次最多。 当传输开始,启动定时器; 停止传输时,停止计时。
是的,有改善此问题的方法。
首先是使用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更新)。 缺点是你占据一个线程的线程池,但是,嘿,这就是他们有什么! 而且,线程关闭当它这样做,因为你设置的完成标志。 你不需要设置,要么当,因为在工作线程额外的运行是在上下文不重要锁定。
你是否尝试过使用,而不是调用的BeginInvoke? 的BeginInvoke()是一个台异步调用。
private void EVENTHANDLER_UpdateTransferProgress(long receivedBytes) {
if(InvokeRequired) {
BeginInvoke(new MethodInvoker(() => {
totalReceivedBytes += receivedBytes;
Label.Text = totalReceivedBytes.ToString("##,0");
}));
}
}