private void btnAction_Click(object sender, EventArgs e)
{
bool Analyze = (cboAnalyzePriceList.SelectedIndex == 0 ? true : false);
bool Preview = (cboPreviewOrSave.SelectedIndex == 0 ? true : false);
Timer_Start();
(rowCnt, FileMatch, ImportId, srcAnalysis) = ProcessActionButton(Analyze, Preview);
PreviewOrSave(Preview);
}
private (int, bool, string, DataTable) ProcessActionButton(bool Analyze, bool Preview)
{
SetupProcessForm(Analyze, Preview);
Thread thProcess = new Thread(() =>
{ (rowCnt, FileMatch, ImportId, srcAnalysis) = mtdUpdateData.ImportValidateAnalyze(ImportId, PriceListFile, MappingName, Analyze, Preview); } );
thProcess.Start();
(rowCnt, ImportId) = FinalizeProcessForm(rowCnt, FileMatch, srcAnalysis);
tbRowCount_Hdr.Text = rowCnt.ToString("N0");
tbImportId_Hdr.Text = ImportId;
Timer_Stop();
return (rowCnt, FileMatch, ImportId, srcAnalysis);
}
新!改进了!
新闻合理且可证明的失败理论醒来了,因为在执行OP指出的过程中可能会阻止线程的外观(即使不是)的外观(即使不是),”
这个新理论是基于这样的观察结果:尽管从理论上不影响UI线程的任务将其卸载到另一个线程中,但如果它们足够强烈,则可以垄断CPU资源。这种繁重的需求不会直接阻止UI线程,而是限制了可用的UI资源以平稳或完全更新。您专门强调了ImportValidateAnalyze(...)
ReadLineAsync()
读取一百万行来改善循环。在这种情况下,人们可能会认为循环会屈服于UI以进行更新。但是,我有两个MRE,可以说明我们“知道”的事物可能并不总是是“ so”!
第一个MRE使用适当的配置Progress
类。
第二MRE使用外部计时器更新,例如原始OP。
如果读取一百万行的循环过于紧密,则可以显示示例可以显示“冻结UI”。我们甚至可以放入一些记录或
DebugWrite
您的最新评论澄清了:
Progress
正如Panagiotis Kanavos在评论中所说的那样,一方面,这似乎是
Progress
ReadLineAsync()
微型可重现示例#2(使用外部更新计时器,例如OP)
此示例使用与原始海报的方法相似的外部更新计时器,以演示在密集的背景处理中管理UI更新。
public partial class MainForm : Form, IProgress<(TimeSpan, int, int)>
{
string VeryLargeFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data", "one-million-guids.txt");
public MainForm()
{
// Make a file with 1 million lines.
if (!File.Exists(VeryLargeFile)) File.WriteAllText(
VeryLargeFile,
string.Join(
Environment.NewLine,
Enumerable.Range(0, 1000000)
.Select(_ => string.Join(
" ",
Enumerable.Range(0, 10).Select(_=>$"{Guid.NewGuid()}")))));
_progress = new Progress<(TimeSpan, int, int)>();
Debug.Assert(SynchronizationContext.Current != null);
_progress.ProgressChanged += (sender, e) =>
{
Debug.Assert(!InvokeRequired);
labelElapsed.Text = $@"{e.Item1:hh\:mm\:ss\.f}";
Text = $"Main Form {e.Item2} of {e.Item3}";
// Uncomment to show that messages 'are' received but do not update the label.
// Debug.WriteLine($"{e.Item2} of {e.Item3}");
};
InitializeComponent();
btnAction.Click += btnAction_Click;
}
Progress<(TimeSpan, int, int)>? _progress;
public void Report((TimeSpan, int, int) value) =>
((IProgress <(TimeSpan, int, int)>?)_progress)?.Report(value);
private async void btnAction_Click(object? sender, EventArgs e)
{
try
{
var cts = new CancellationTokenSource();
btnAction.Enabled = false;
labelElapsed.Visible = true;
await ImportValidateAnalyze(this, cts.Token);
}
catch(OperationCanceledException)
{ }
finally
{
btnAction.Enabled = true;
labelElapsed.Visible = false;
}
}
private async Task ImportValidateAnalyze(IProgress<(TimeSpan, int, int)> progress, CancellationToken token)
{
var stopwatch = Stopwatch.StartNew();
var lastUpdate = 0;
// "The ImportValidateAnalyze() method may import one million lines from a text file..."
var count = 0;
var max = 1000000;
progress.Report((TimeSpan.Zero, count, max));
using (StreamReader reader = new StreamReader(VeryLargeFile))
{
while (count < max)
{
if (await reader.ReadLineAsync(token) is string line)
{
count++;
var currentUpdate = (int)(stopwatch.Elapsed.TotalSeconds * 10);
if (checkBoxBreakMe.Checked)
{
// Without throttling.
progress.Report((stopwatch.Elapsed, count, max));
}
else
{
if (lastUpdate < currentUpdate)
{
// Throttle updates to 0.1 second intervals.
progress.Report((stopwatch.Elapsed, count, max));
lastUpdate = currentUpdate;
}
}
}
else break;
}
}
progress.Report((stopwatch.Elapsed, max, max));
}
}