BackgroundWorker.ReportProgress 报告错误值

发布于 2024-11-02 09:34:45 字数 4403 浏览 3 评论 0原文

我有一个类从大文件(100,000)行中提取字符串,并希望向用户提供一些进度反馈。我的问题是进度报告不正确。

我正在使用自定义类发送 UserState 属性:

public enum UpdateType {FileCount, ProcessedFiles, LineCount, ProcessedLines, StepUpdate};
public class ProgressUpdate
{
        public UpdateType UpdateType { get { } set { } }
        public string Update { get { } set { } }
}

这是我处理 ReportProgress 事件的方法:

public class BGWorkerBase : BackgroundWorker
{
    public void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        ProgressUpdate update;
        update = (ProgressUpdate)e.UserState;
        switch (update.UpdateType)
        {
            case UpdateType.FileCount:
                MainWindow.FrmDisplayProgress.FileCount = update.Update;
                break;

            case UpdateType.ProcessedFiles:
                MainWindow.FrmDisplayProgress.FileProgress = update.Update;
                break;

            case UpdateType.LineCount:
                MainWindow.FrmDisplayProgress.LineCount = update.Update;
                break;

            case UpdateType.ProcessedLines:
                MainWindow.FrmDisplayProgress.LineProgress = update.Update;
                MainWindow.FrmDisplayProgress.PrecentProgress = e.ProgressPercentage;
                break;

            case UpdateType.StepUpdate:
                MainWindow.FrmDisplayProgress.AddLine(update.Update);
                break;
        }
    }
}

MainWindow.FrmDisplayProgress 是对显示进度的表单的调用。 最后,这是工作类:

public class TraceFile
{
    public HashSet<string> ExtractValues(HashSet<MyRegex> regexList, BackgroundWorker worker)
    {
        HashSet<string> results = new HashSet<string>();
        int rowNumber = 1;

        foreach (DataRow row in Lines.Rows)
        {
            int percentComplete = (int)(((float)rowNumber / (float)Lines.Rows.Count) * 100);
            worker.ReportProgress(percentComplete, new ProgressUpdate(UpdateType.ProcessedLines, rowNumber++.ToString()));

            // using multiple regex supports different formats for the same data type. For example - more then 1 account format
            foreach (MyRegex regex in regexList)
            {
                MatchCollection matches = regex.Matches(row["Text"].ToString());
                if (matches.Count > 0)
                {
                    foreach (Match result in matches)
                        results.Add(result.ToString());
                }
            }
        }

        return results;
    }
}

这种情况捕获这些特定类型的更新:

case UpdateType.ProcessedLines:
                    MainWindow.FrmDisplayProgress.LineProgress = update.Update;
                    MainWindow.FrmDisplayProgress.PrecentProgress = e.ProgressPercentage;
                    break;

'MainWindow.FrmDisplayProgress.PrecentProgress = e.ProgressPercentage;'正在更新进度条。进度条不是从 0 缓慢移动到 100 一次,而是从 0 快速移动到 100 几次。 'MainWindow.FrmDisplayProgress.LineProgress = update.Update' 正在使用行号更新标签,但它没有执行任何操作。 由于某种原因,在调试模式下我看到两者都正确更新,因此我怀疑存在一些线程问题。

我希望我能够以清晰的方式呈现这一切。

问题的解决方案:

  1. 进度条运行多次是转移注意力,与问题无关。发生这种情况是因为该方法被输入了多次。
  2. 标签未更新是由于调用过于频繁(有关更多详细信息,请参阅下面此问题的答案)。我将方法更改为:

    public HashSet; ExtractValues(HashSet  regexList,BackgroundWorker工人)
    {
        HashSet<字符串>结果 = new HashSet();
        int 行号 = 0;
        日期时间开始计数 = 日期时间.Now;
        日期时间结束计数 = 日期时间.Now;
    
        foreach(Lines.Rows 中的 DataRow 行)
        {
            行号++;
            TimeSpan timeSpan = endCount.Subtract(startCount);
            if ((timeSpan.Milliseconds > 50) | (rowNumber == Lines.Rows.Count))
            {
                int 百分比完成 = (int)(((float)rowNumber / (float)Lines.Rows.Count) * 100);
                worker.ReportProgress(percentComplete, new ProgressUpdate(UpdateType.ProcessedLines, rowNumber.ToString()));
                startCount = 日期时间.Now;
            }
    
            // 使用多个正则表达式支持同一数据类型的不同格式。例如 - 超过 1 个帐户格式
            foreach(regexList 中的 MyRegex 正则表达式)
            {
                MatchCollection matches = regex.Matches(row["Text"].ToString());
                if (matches.Count > 0)
                {
                    foreach(匹配中的匹配结果)
                        结果.Add(结果.ToString());
                }
            }
    
            endCount = 日期时间.Now;
        }
    
        返回结果;
    }
    

I have a class extracting strings from a large file (100,000) lines and wanted to give some progress feedback to the user. My problem is that the progress is not reported correctly.

I am using a custom class to send in the UserState property:

public enum UpdateType {FileCount, ProcessedFiles, LineCount, ProcessedLines, StepUpdate};
public class ProgressUpdate
{
        public UpdateType UpdateType { get { } set { } }
        public string Update { get { } set { } }
}

This is my method for handling the ReportProgress event:

public class BGWorkerBase : BackgroundWorker
{
    public void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        ProgressUpdate update;
        update = (ProgressUpdate)e.UserState;
        switch (update.UpdateType)
        {
            case UpdateType.FileCount:
                MainWindow.FrmDisplayProgress.FileCount = update.Update;
                break;

            case UpdateType.ProcessedFiles:
                MainWindow.FrmDisplayProgress.FileProgress = update.Update;
                break;

            case UpdateType.LineCount:
                MainWindow.FrmDisplayProgress.LineCount = update.Update;
                break;

            case UpdateType.ProcessedLines:
                MainWindow.FrmDisplayProgress.LineProgress = update.Update;
                MainWindow.FrmDisplayProgress.PrecentProgress = e.ProgressPercentage;
                break;

            case UpdateType.StepUpdate:
                MainWindow.FrmDisplayProgress.AddLine(update.Update);
                break;
        }
    }
}

MainWindow.FrmDisplayProgress is a call to a form that displays progress.
And last, this is the worker class:

public class TraceFile
{
    public HashSet<string> ExtractValues(HashSet<MyRegex> regexList, BackgroundWorker worker)
    {
        HashSet<string> results = new HashSet<string>();
        int rowNumber = 1;

        foreach (DataRow row in Lines.Rows)
        {
            int percentComplete = (int)(((float)rowNumber / (float)Lines.Rows.Count) * 100);
            worker.ReportProgress(percentComplete, new ProgressUpdate(UpdateType.ProcessedLines, rowNumber++.ToString()));

            // using multiple regex supports different formats for the same data type. For example - more then 1 account format
            foreach (MyRegex regex in regexList)
            {
                MatchCollection matches = regex.Matches(row["Text"].ToString());
                if (matches.Count > 0)
                {
                    foreach (Match result in matches)
                        results.Add(result.ToString());
                }
            }
        }

        return results;
    }
}

This is the case that catches these specific type of updates :

case UpdateType.ProcessedLines:
                    MainWindow.FrmDisplayProgress.LineProgress = update.Update;
                    MainWindow.FrmDisplayProgress.PrecentProgress = e.ProgressPercentage;
                    break;

'MainWindow.FrmDisplayProgress.PrecentProgress = e.ProgressPercentage;' is updating a progress bar. Instead of slowly moving from 0 to 100 once, the progress bar moves from 0 to 100 quickly several times.
'MainWindow.FrmDisplayProgress.LineProgress = update.Update' is updating a label with the line number but its not doing anything.
For some reason, while in debug mode I saw both updating correctly so I'm suspecting some threading issues.

I hope I managed to present all this in a clear manner.

Solution for the issue:

  1. The progress bar running several times was a red herring and is not related to the issue. This happened becasue the method was entered several times.
  2. The label not updating is due to too frequent calls (see the answer to this question below for more details). I changed the method to this:

    public HashSet<string> ExtractValues(HashSet<MyRegex> regexList, BackgroundWorker worker)
    {
        HashSet<string> results = new HashSet<string>();
        int rowNumber = 0;
        DateTime startCount = DateTime.Now;
        DateTime endCount = DateTime.Now;
    
        foreach (DataRow row in Lines.Rows)
        {
            rowNumber++;
            TimeSpan timeSpan = endCount.Subtract(startCount);
            if ((timeSpan.Milliseconds > 50) | (rowNumber == Lines.Rows.Count))
            {
                int percentComplete = (int)(((float)rowNumber / (float)Lines.Rows.Count) * 100);
                worker.ReportProgress(percentComplete, new ProgressUpdate(UpdateType.ProcessedLines, rowNumber.ToString()));
                startCount = DateTime.Now;
            }
    
            // using multiple regex supports different formats for the same data type. For example - more then 1 account format
            foreach (MyRegex regex in regexList)
            {
                MatchCollection matches = regex.Matches(row["Text"].ToString());
                if (matches.Count > 0)
                {
                    foreach (Match result in matches)
                        results.Add(result.ToString());
                }
            }
    
            endCount = DateTime.Now;
        }
    
        return results;
    }
    

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

初熏 2024-11-09 09:34:46

您调用 ReportProgress 的次数太频繁了。每秒调用它的频率超过大约一千次,UI 线程就会被调用请求淹没,以至于无法再执行其常规职责。它停止绘画并关注用户输入。更新仍然会发生,只是你再也看不到它们了。

当然会发生这种情况,因为您在内循环中有 ReportProgress,为数据集中的每一行调用它。这是浪费精力,人眼不可能跟上这个更新速度,任何超过每秒 20 次的更新看起来都是模糊的。 Q&D 修复方法是为每第 100 行调用 ReportProgress。注意已用时间并计算 45 毫秒是适用于任何计算机的修复方法。

进度条的问题听起来像是一个计算问题,从代码片段来看并不明显。进度条起作用但文本更新不起作用的原因是 PB 的设计就是为了避免这种绘制问题,它在 Value 属性发生变化时强制重新绘制,而不是等待 Windows 传递 Paint 通知。

You are calling ReportProgress too often. Call it more frequently than about a thousand times per second and the UI thread gets so swamped by the invoke requests that it doesn't get around to its regular duties anymore. It stops painting and paying attention to user input. The updates still happen, you just can't see them anymore.

This of course happens because you have ReportProgress inside the inner loop, calling it for each individual row in the dataset. It is wasted effort, the human eye cannot possibly keep up with that rate of updates, anything beyond 20 updates per second looks like a blur. A Q&D fix would be to call ReportProgress for every 100th row. Paying attention to elapsed time and count off 45 msec is the fix that works on any machine.

The problem with the progress bar sounds like a calculation problem, one that's not obvious from the snippet. The reason the progress bar works but not the text update is that PB was designed to avoid this painting problem, it forces a repaint when its Value property changes instead of waiting for Windows to deliver the Paint notification.

南烟 2024-11-09 09:34:46

而不是慢慢地从0移动到100
一旦进度条从 0 移动到
100快几次。

您是否将进度条的MinimumMaximum设置为0和100?
WPF ProgressBar 的默认值为 0 且1.

如果您使用 WinForms 进度条 默认值是 0 和 100,这不应该是问题。

Instead of slowly moving from 0 to 100
once, the progress bar moves from 0 to
100 quickly several times.

Did you set the Minimum and Maximum of the progress bar to 0 and 100?
The defaults for a WPF ProgressBar is 0 and 1.

In case you are use a WinForms progress bar the default is 0 and 100, and that shouldn't be the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文