C # Progress Bar Invoke / Делегатский вопрос - PullRequest
4 голосов
/ 07 декабря 2011

Я передаю файлы и хотел бы, чтобы индикатор выполнения отображал фактический прогресс каждого файла. Это работает нормально для файлов до 15 мегабайт, но файлы, размер которых больше этого, могут привести к зависанию моего приложения. Если я не вызову этот код для индикатора выполнения, эти большие файлы будут передаваться просто отлично.

Я пробовал всевозможные способы справиться с этим с делегатами, но не повезло. Скорее, они отлично работают с небольшими файлами, но не с большими.

Некоторые примеры, которые сработали ...

pbFileProgress.Invoke((MethodInvoker)
   delegate 
   { 
      pbFileProgress.Value = args.PercentDone;
   });                

Кроме того, этот набор методов работал для небольших файлов.

private delegate void SetProgressBarCallback(int percentDone);

public void UpdateProgressBar(object send, UploadProgressArgs args)
{
   if (pbFileProgress.InvokeRequired)
   {
      var d = new SetProgressBarCallback(ProgressBarUpdate);
      BeginInvoke(d, new object[] { args.PercentDone });
   }
   else
   {
      ProgressBarUpdate(args.PercentDone);
   }
}

public void ProgressBarUpdate(int percentDone)
{
   pbFileProgress.Value = percentDone;
}

Но опять же, все просто зависает, если я пытаюсь увеличить файлы.

Ответы [ 3 ]

2 голосов
/ 07 декабря 2011

Несмотря на отсутствие контекста, вот пример, который работает.Метод BeginInvoke или Invoke вызывается максимум в 100 раз.

Task.Factory.StartNew(() =>
   {
      using (var source = File.OpenRead(@"D:\Temp\bbe.wav"))
      using (var destination = File.Create(@"D:\Temp\Copy.wav"))
      {
         var blockUnit = source.Length / 100;

         var total = 0L;
         var lastValue = 0;

         var buffer = new byte[4096];
         int count;

         while ((count = source.Read(buffer, 0, buffer.Length)) > 0)
         {
            destination.Write(buffer, 0, count);

            total += count;

            if (blockUnit > 0 && total / blockUnit > lastValue)
            {
               this.BeginInvoke(
                  new Action<int>(value => this.progressBar1.Value = value),
                  lastValue = (int)(total / blockUnit));
            }
         }

         this.BeginInvoke(
            new Action<int>(value => this.progressBar1.Value = value), 100);
      }
   });
1 голос
/ 07 декабря 2011

Эта проблема очень распространена при обмене данными между фоновым потоком и потоками переднего плана: фоновый поток отправляет потоку переднего плана слишком много обновлений.

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

Существует несколько решений этой проблемы, но я настоятельно рекомендую использовать Timer в потоке переднего плана для опроса фонового хода и обновления пользовательского интерфейса.
Преимущество использования Timer:

  • Фоновый поток может сообщать о прогрессе так часто, как это необходимо
  • Тема переднего плана может просто расслабиться, пока не потребуется обновление
  • Поток переднего плана не будет "создавать резервные копии" с обновлениями
  • Если поток переднего плана «отдыхает», то поток фона получает больше процессорного времени
  • Частота Timer может быть установлена ​​на «разумное» значение, например 250 мс (4 обновления в секунду), так, чтобы прогресс был плавным, но не занимал весь процессор

Как всегда, безопасность потоков важна при передаче прогресса между потоками. Использование простого 32-разрядного значения int в этом сценарии является поточно-ориентированным, но использование 32-разрядного double не является поточно-ориентированным на 32-разрядных компьютерах.

0 голосов
/ 07 декабря 2011

Вы можете вызвать на основе элемента пользовательского интерфейса. Например:

private delegate void InvokeUpdateProgressBar(object send, UploadProgressArgs args);
private int _PercentDone = -1;

public void UpdateProgressBar(object send, UploadProgressArgs args)
{
   if(_PercentDone != args.PercentDone)
   {
      if (pbFileProgress.InvokeRequired)
      {
         pbFileProgress.Invoke(
            new InvokeUpdateProgressBar(UpdateProgressBar),
            new object[] { send, args });
      }
      else
      {
         ProgressBarUpdate(args.PercentDone);
      }
      _PercentDone = args.PercentDone;
   }
}

В противном случае я бы предложил то, что сделал Аарон Макивер, и использовал бы класс BackgroundWorker . См. Пример здесь для получения подробной информации об обновлении индикатора выполнения с помощью класса BackgroundWorker

Обновление Похоже, вы не единственный с этой проблемой. См. Amazon s3 Transferutility. Загрузка зависает в C # здесь. Кент также указывает, говорит: If you read in about the S3 forums you'll find many people having similar issues

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...