我在下面的代码中使用Process2()方法报告进度时遇到问题。我想在读取的每一行之后增加进度条,但是这样做会阻塞UI,并且它变得无响应。如果我注释掉progress.Report()行,它将不再阻塞UI线程。有谁知道为什么会发生这种情况以及我该如何解决?
这里是可以正常工作的代码,可以粘贴到入门WPF应用程序中。
单击“运行”按钮(开始生成文件时可能会稍作停顿,等到“完成生成文件”之后),然后尝试将窗口移至另一处,该窗口将保持冻结状态。
警告:此代码将在bin \ Debug文件夹(或配置指向的任何文件)中生成一个文本文件,如果您从网络路径运行此文件,则可能无法写入此文件,因此建议从以下位置运行本地磁盘。
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Queue<string> queue = new Queue<string>();
List<string> stringsCollection = new List<string>() { "1_abc123_A_AA_zzz", "2_abc123_AAAA_zzz", "3_abc123_AAAAAA_zzz" };
int linesCount = 0;
int totalLines = 0;
string ASSEMBLY_PATH;
string file;
public MainWindow()
{
InitializeComponent();
ASSEMBLY_PATH = ReturnThisAssemblyPath();
file = ASSEMBLY_PATH + @"\test.txt";
generateFile();
}
private async void Button_Click2(object sender, RoutedEventArgs e)
{
linesCount = 0;
Progress<int> process2_progress;
this.progress.Value = 0;
this.status.Text = "";
process2_progress = new Progress<int>();
process2_progress.ProgressChanged += Process2_progress_ProgressChanged;
this.status.Text += "Searching..." + Environment.NewLine;
await Task.Run(() =>
{
totalLines = System.IO.File.ReadLines(file).Count();
foreach (string s in stringsCollection)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
this.status.Text += "Searching " + s + Environment.NewLine;
}));
List<string> strCollection = Process2(s, process2_progress);
foreach (string str in strCollection)
queue.Enqueue(str);
}
});
this.status.Text += "DONE!!" + Environment.NewLine;
}
private void Process2_progress_ProgressChanged(object sender, int e)
{
linesCount += e;
this.progress.Value = linesCount * 100 / totalLines;
}
List<string> Process2(string inputString, IProgress<int> progress)
{
List<string> result = new List<string>();
foreach (string line in System.IO.File.ReadLines(file, new UTF8Encoding()))
{
progress.Report(1);
}
return result;
}
void generateFile()
{
this.status.Text += "Generating FIle..." + Environment.NewLine;
int count = 0;
using (StreamWriter sw = new StreamWriter(file, true))
{
do
{
sw.WriteLine(Guid.NewGuid().ToString());
count++;
} while (count < 51000);
}
this.status.Text += "Done Generating FIle!" + Environment.NewLine;
}
public string ReturnThisAssemblyPath()
{
string codeBase = Assembly.GetAssembly(typeof(MainWindow)).CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return System.IO.Path.GetDirectoryName(path);
}
}
}
MainWindow.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="status" Grid.Row="0"></TextBox>
<Button Grid.Row="2" Height="50" Click="Button_Click2">Run2</Button>
<ProgressBar x:Name="progress" Grid.Row="3" Height="20" ></ProgressBar>
</Grid>
</Window>
答案 0 :(得分:6)
我怀疑您的问题是您过于频繁地报告进度。如果您在<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<div class="container">
<div id="ReactivateAlert" style="display:none">
<div class="alert alert-danger alert-dismissible" role="alert">
When this alert box appears, the margin does not get included in the animation.
</div>
</div>
<div class="col-md-2">
<label class="col-form-label col-md-2" for="SelectDemo">Table</label>
</div>
<div class="col-md-10">
<select class="form-control" id="SelectDemo" name="SelectDemo">
<option value="">--- Select a Test ---</option>
<option value="Test1">Test1</option>
<option value="Test2">Test2</option>
<option value="Test3">Test3</option>
<option value="Test4~">Test4~</option>
<option value="Test5~">Test5~</option>
<option value="Test6~">Test6~</option>
</select>
</div>
</div>
调用之间所做的工作是微不足道的(例如,仅从文件中读取一行),那么向UI线程分配操作将成为您的瓶颈。您的UI调度程序队列已泛滥,无法跟上新事件的发生,例如响应鼠标单击或移动。
为减轻这种情况,您应该将Report
呼叫的频率降低到合理的水平-例如,仅在处理了1,000条线路时才调用它。
Report
针对评论:选择批处理大小时文件大小无关紧要。而是:为更新频率找到一个合理的目标,例如100µms。测量或估计读取和处理一条线所需的时间-例如100μs。将前者除以后者,您将得到答案。我们之所以选择1,000条,是因为我们估计1,000条线需要100µms的处理时间。最佳更新频率在10–100µms左右,这是人类感知的极限。用户不会注意到任何比这更频繁的事情。
根据以上所述,您的10行和500行文件不需要对UI发出任何更新,因为在用户有机会观察到任何进度之前,它们将在短短几毫秒内就被完全处理。 1,000,000行的文件总共需要大约100秒的时间,在此期间它将更新UI 1,000次(每100µms一次)。