C#进度条调用/委托问题

时间:2011-12-06 20:08:12

标签: c# progress-bar

我正在传输文件,并希望进度条显示每个文件的实际进度。这适用于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 个答案:

答案 0 :(得分:2)

尽管缺乏背景,但这是一个有效的例子。 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 :(得分:1)

在后台线程和前台线程之间进行通信时,此问题非常常见:后台线程正在发送前台线程太多更新。

前台线程处理更新,绘图和用户输入,因此当有太多更新时,UI会冻结以尝试赶上。
显然,如果后台线程继续发送更新,那么在后台任务完成后,即使也可以备份前台!

此问题有多种解决方案,但我最强烈的建议是在前台线程中使用Timer轮询后台进度并更新用户界面。
使用Timer

的优势
  • 后台主题可以根据需要频繁报告进度
  • 前台线程可以放松,直到需要更新
  • 前台线程不会“备份”更新
  • 如果前台线程处于“休息”状态,则后台线程会获得更多处理器时间
  • Timer的频率可以设置为“合理”值,例如250毫秒(每秒4次更新),以便进度顺利但不占用整个处理器

与往常一样,在线程之间传递进度时,线程安全很重要。在这种情况下,使用简单的32位int值是线程安全的,但使用64位double在32位计算机上不是线程安全的。

答案 2 :(得分:0)

您可以根据UI元素进行调用。例如:

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;
   }
}

否则我会建议Aaron McIver做了什么并使用BackgroundWorker类。 有关使用BackgroundWorker类更新进度条的详细信息,请参阅示例here

<强>更新 看起来你不是唯一有这个问题的人。请在此处查看Amazon s3 Transferutility.Upload hangs in C#。肯特还指出:If you read in about the S3 forums you'll find many people having similar issues