BackgroundWorker.ReportProgress сообщает о неправильных значениях - PullRequest
0 голосов
/ 19 апреля 2011

У меня есть класс, извлекающий строки из большого файла (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<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;
    }
    

Ответы [ 2 ]

3 голосов
/ 19 апреля 2011

Вы слишком часто вызываете ReportProgress. Вызывайте его чаще, чем примерно тысячу раз в секунду, и поток пользовательского интерфейса настолько захлёстывается запросами вызова, что больше не может выполнять свои обычные обязанности. Останавливает рисование и обращает внимание на ввод пользователя. Обновления все еще происходят, вы просто больше их не видите.

Это, конечно, происходит, потому что у вас есть ReportProgress во внутреннем цикле, вызывая его для каждой отдельной строки в наборе данных. Это бесполезное усилие, человеческий глаз не может идти в ногу с такой частотой обновлений, что-то, превышающее 20 обновлений в секунду, выглядит как размытие. Исправление Q & D будет вызывать ReportProgress для каждой сотой строки. Уделение внимания прошедшему времени и отсчет 45 мсек - это исправление, которое работает на любой машине.

Проблема с индикатором выполнения звучит как проблема вычислений, которая не очевидна из фрагмента. Причина, по которой работает индикатор выполнения, но не текстовое обновление, заключается в том, что PB был разработан, чтобы избежать этой проблемы рисования, и заставляет перерисовывать при изменении свойства Value вместо ожидания, пока Windows выдаст уведомление Paint.

0 голосов
/ 19 апреля 2011

Вместо медленного перехода от 0 до 100 один раз, индикатор выполнения перемещается от 0 до 100 быстро несколько раз.

Вы установили Minimum и Maximum индикатора выполнения на 0 и 100? Значения по умолчанию для WPF ProgressBar - 0 и 1 .

Если вы используете индикатор выполнения WinForms , по умолчанию используется 0 и 100 , и это не должно быть проблемой.

...