【发布时间】:2018-04-20 10:32:08
【问题描述】:
我只是在研究 C# 和 WPF。
我正在开发一个应在 LAN 上处理数据传输的应用程序。
特别是,对于每个传输进度条都会显示,如果是下载则为绿色,如果是上传则为红色。
最初我做了一些虚拟的尝试来模拟传输(网络的线程被停用)。所以这些条都是以编程方式填充的,我检查了 GUI 的性能,一切都很好。特别是我可以选择这些条并显示一个上下文菜单。
经过一段时间后,我们开发了网络部分,主要是读写 TCP。对于每个块(或块组),都会执行一个执行步骤,以便进度条前进。
块的数量因文件而异,但如果块超过 100 个,则要执行的步骤数始终为 100,这样每次传输始终执行 100 或更少的执行步骤。
问题是,当真正的传输开始时,一切都变慢了,当我将窗口拖到四周并且 GUI 不再响应时,我什至无法单击栏以显示上下文菜单。但是,条形图的进度是有效的。
我们尝试禁用栏并进行传输,GUI 响应迅速且没有问题。
可能我们从网络损坏了 GUI 刷新。
网络有自己的线程,而 GUI 另一个。
通过网络刷新栏的最佳方法是什么?
我们目前使用这个,但我们不认为这是一个好方法,肯定它没有效率。
例如我们取网络客户端代码:
...
// Object representing a transfer (shared between GUI and Network)
Transfer t = new Transfer(...);
int bytesRead;
var buffer = new byte[chunkSize];
while ((bytesRead = file.Read(buffer, 0, buffer.Length)) > 0)
{
if (t.Stop)
{
break;
}
nwStream.Write(buffer, 0, bytesRead);
PBcount++;
if (PBcount == PBchuks)
{
t.PerformStep(); // Make the progress bar to advance, works, but laggy
PBcount = 0;
}
}
...
并且与 PerformStep() 相关的代码是 Transfer 共享对象的公共方法:
public void PerformStep()
{
CurrentStep = CurrentStep + 1;
Application.Current.Dispatcher.Invoke(delegate {
MainWindow wnd = (MainWindow)Application.Current.MainWindow;
wnd?.PerformStepProgressBarRefresh();
});
}
以下是委托刷新 ListBox 项的 MainWindow GUI 方法的代码:
public void PerformStepProgressBarRefresh()
{
Application.Current.Dispatcher.Invoke(delegate
{
TransfersXAML.Items.Refresh();
});
}
不知道有没有用,不过还是放一下吧,就是进度条相关的XAML代码:
<ListBox x:Name="TransfersXAML"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Transfers}">
<ListBox.ItemTemplate>
<DataTemplate>
<ProgressBar Height="30" Minimum="0"
Maximum="{Binding NSteps}"
Value="{Binding CurrentStep}"
Foreground="{Binding Color}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Get info" Click="GetTransferInfoClick" />
<MenuItem Header="Cancel" Click="CancelTransferClick" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
您知道更好、更高效的系统,以便网络的线程可以以某种方式更新进度条吗?
毕竟,我们不知道我们做的是否正确。
我们可能没有提供所有有助于更好地理解问题的信息:发出信号以便我们更新问题。
更新
public class Transfer
{
public enum Type_t
{
upload,
download
}
public int NSteps { get; set; }
public int CurrentStep { get; set; }
public Type_t Type { get; set; }
public string Color { get; set; }
public string ID { get; set; }
public string SenderID { get; set; }
public string ReceiverID { get; set; }
public Transfer(string senderID, string receiverID, Type_t _type, string _color)
{
SenderID = senderID;
ReceiverID = receiverID;
NSteps = 100;
CurrentStep = 0;
Type = _type;
Color = _color;
ID = Utils.GetHashString(senderID + receiverID + Utils.GetCurrentTimestamp());
}
public void PerformStep()
{
CurrentStep = CurrentStep + 1;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
MainWindow wnd = (MainWindow)Application.Current.MainWindow;
wnd?.PerformStepProgressBarRefresh();
}));
}
}
【问题讨论】:
-
如果您使用“Dispatcher.BeginInvoke”而不是“Dispatcher.Invoke()”,情况会如何改变?
-
如果绑定到
ProgressBar中的CurrentStep,为什么要调用刷新进度条的方法?CurrentStep不调用 OnPropertyChanged 事件吗 -
我会先删除那个可疑的 Refresh。
-
正如@Jesper 所建议的那样,使用“BeginInvoke”而不是“Invoke”解决了我的问题。
-
但是,作为您提到的可疑刷新,我也对其他主题持开放态度。为什么要怀疑?
标签: c# multithreading xaml networking graphics