异步方法运行速度太快,无法在主线程上更新我的列表框 [重复]

Posted

技术标签:

【中文标题】异步方法运行速度太快,无法在主线程上更新我的列表框 [重复]【英文标题】:Async method running too fast to update my listbox on the main thread [duplicate] 【发布时间】:2021-04-10 13:36:24 【问题描述】:

我有一个 WPF 按钮和一个列表框。当我按下按钮时,它使用异步并等待运行数字生成器。数字生成器只是一个生成整数的 for 循环。循环中还有一个 Iprogress 报告整数。主线程中的一个函数,更新 UI 中的 listBox。

问题是列表框没有正确更新,但是如果我将 Thread.sleep(10) 放在 for 循环中,它可以正常工作。看起来线程生成报告的速度太快,列表框无法更新。如何取消 Thread.sleep(10) 并忠实地更新列表框?我以正确的方式接近这个吗?有人可以帮忙吗?谢谢。

    public partial class MainWindow : Window

    Progress<ProgressReportModel> progress = new Progress<ProgressReportModel>();
    public MainWindow()
    
        InitializeComponent();
        progress.ProgressChanged += ReportProgress;
       
    private async void generateNumbersButton_Click(object sender, RoutedEventArgs e)
    
        await Task.Run(()=>NumberGenerator.SimpleNumberGenerator(progress));
    
    private void ReportProgress(object sender, ProgressReportModel e)
    
        numbersListBox.Items.Add($"Integer generated e.LastIntegerGenerated.ToString()");
    



static class NumberGenerator

    static public void SimpleNumberGenerator(IProgress<ProgressReportModel> progress)
    
        ProgressReportModel report = new ProgressReportModel();
        int number = 10;
        for (int i = 0; i <= number ; i++)
        
            report.LastIntegerGenerated = i;
            progress.Report(report);
            //Thread.Sleep(10);
        
    


public class ProgressReportModel

    public int LastIntegerGenerated  get; set;  = 0;

run with no Thread.Sleep in the loop. Expecting integers 0 to 10 but getting this

run with Thread.Sleep(3)

run with Thread.Sleep(10) and it the numbers are registered faithfully

【问题讨论】:

链接不相关。您无需将Thread.Sleep 放在reporting 线程中。 任何时候有人说“我使用了一个随机数生成器,当我缓慢运行代码时它工作正常[比如步进调试器],但是当我全速运行 prod 代码时它不起作用!”我立刻想知道“你是在从时钟播种你的 RNG 吗?” - 这里有很多 Q,有人在循环中声明了 new Random(),但没有意识到连续大约 1000 次循环迭代,代码比时钟分辨率快,所以 RNG 以相同的时间值播种,破坏了随机性 WinForms 和 WPF 控件都支持数据绑定。不要显式地将项目添加到列表框,而是将列表框绑定到要显示的数据容器,例如List&lt;T&gt;ObservableCollection&lt;T&gt;。将数据添加到该容器并让 UI 自行更新。不过,在紧密循环中重绘 UI 代价高昂,因此您可能希望以某种方式限制重绘操作。 你正在发布和修改同一个 ProgressReportModel 实例......所以很明显,如果程序点击 ReportProgress 它可能具有不同的值,然后它被发布时 我刚刚意识到这个问题从未解释过什么是错的。我没有注意到所有Report 调用中都使用了 same 实例。这意味着 same 对象被多次添加到列表框中,导致列表框中的值相同。过于频繁的重绘是个问题,但报告错误值更糟糕。 【参考方案1】:

基本上,问题在于 ReportProgress 方法在报告对象的 LastIntegerGenerated 属性得到更新之后被调用

解决问题的最简单方法是为每次调用progress.Report 使用一个新的报表对象。移动线:

ProgressReportModel report = new ProgressReportModel();

在你的 for 循环中,你应该不再需要延迟。

【讨论】:

Progress<T> 是一个基本的 .NET BCL 类,用于以线程安全的方式报告异步操作的进度

以上是关于异步方法运行速度太快,无法在主线程上更新我的列表框 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 AsyncTask 在主线程上运行?

C#异步编程----Thread

使用异步任务时光标变为空?

python随用随学20200220-异步IO

android从网络上异步加载图像

如何在android一条单独线程,更新ui ?