gpt4 book ai didi

c# - 异步方法的延迟进度报告

转载 作者:行者123 更新时间:2023-12-02 02:34:06 25 4
gpt4 key购买 nike

我有一个包含 Button 和 RichTextBox 控件的 WinForms 应用程序。用户点击Button后,执行IO要求较高的操作。为了防止 UI 线程阻塞,我实现了异步/等待模式。我还想向 RichTextBox 报告此操作的进度。简化后的逻辑如下所示:

private async void LoadData_Click(Object sender, EventArgs e)
{
this.LoadDataBtn.Enabled = false;

IProgress<String> progressHandler = new Progress<String>(p => this.Log(p));

this.Log("Initiating work...");

List<Int32> result = await this.HeavyIO(new List<Int32> { 1, 2, 3 }, progressHandler);

this.Log("Done!");

this.LoadDataBtn.Enabled = true;
}

private async Task<List<Int32>> HeavyIO(List<Int32> ids, IProgress<String> progress)
{
List<Int32> result = new List<Int32>();

foreach (Int32 id in ids)
{
progress?.Report("Downloading data for " + id);

await Task.Delay(500); // Assume that data is downloaded from the web here.

progress?.Report("Data loaded successfully for " + id);

Int32 x = id + 1; // Assume some lightweight processing based on downloaded data.

progress?.Report("Processing succeeded for " + id);

result.Add(x);
}

return result;
}

private void Log(String message)
{
message += Environment.NewLine;
this.RichTextBox.AppendText(message);
Console.Write(message);
}

操作成功完成后,RichTextBox 包含以下文本:

Initiating work...
Downloading data for 1
Data loaded successfully for 1
Processing succeeded for 1
Downloading data for 2
Data loaded successfully for 2
Processing succeeded for 2
Downloading data for 3
Done!
Data loaded successfully for 3
Processing succeeded for 3

正如您所看到的,第三个工作项的进度是在完成!之后报告的。

我的问题是,是什么导致进度报告延迟?如何才能实现 LoadData_Click 流程仅在报告所有进度后才继续?

最佳答案

Progress 类将在创建时捕获当前同步上下文,然后将回调发布到该上下文(该类的文档中有说明,或者您可以查看源代码)。在您的情况下,这意味着 WindowsFormsSynhronizationContext 被捕获,并且发布到它与执行 Control.BeginInvoke() 大致相同。

await 还捕获当前上下文(除非您使用 ConfigureAwait(false))并将方法的延续发布到它。对于除最后一次之外的迭代,UI 线程在 await Task.Delay(500); 上释放,因此可以处理您的报告回调。但在 foreach 循环的最后一次迭代中,会发生以下情况:

// context is captured
await Task.Delay(500); // Assume that data is downloaded from the web here.
// we are now back on UI thread
progress?.Report("Data loaded successfully for " + id);
// this is the same as BeginInvoke - this puts your callback in UI thread
// message queue
Int32 x = id + 1; // Assume some lightweight processing based on downloaded data.
// this also puts callback in UI thread queue and returns
progress?.Report("Processing succeeded for " + id);
result.Add(x);

因此,在上一次迭代中,您的回调被放入 UI 线程消息队列中,但它们现在无法执行,因为您同时正在 UI 线程中执行代码。当代码到达 this.Log("done") 时,它会写入您的日志控件(此处不使用 BeginInvoke)。然后,在您的 LoadData_Click 方法结束后 - 仅在此时 UI 线程才从执行您的代码中释放,并且消息队列可能会被处理,因此等待在那里的 2 个回调将得到解决。

鉴于所有这些信息 - 只需像 Enigmativity 在评论中所说的那样直接Log - 此处无需使用 Progress 类。

关于c# - 异步方法的延迟进度报告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47472360/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com