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

本文关键字:问题 调用 | 更新日期: 2023-09-27 18:16:53

我正在传输文件,并希望进度条显示每个文件的实际进度。这对于小于15兆的文件工作得很好,但大于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;
}

但是,如果我尝试更大的文件,一切都会死机

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

尽管缺乏上下文,但这里有一个可行的示例。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);
      }
   });

这个问题在后台和前台线程之间通信时非常常见:后台线程发送前台线程太多更新。

前台线程处理更新,绘图和用户输入,所以当太多的更新进来时,UI冻结试图赶上。
显然,如果后台线程继续发送更新,前台甚至可以在后台任务完成后备份 !

这个问题有几种解决方案,但我强烈建议在前台线程中使用Timer轮询后台进程并更新UI。
使用Timer的优点:
  • 后台线程可以根据需要频繁地报告进程
  • 前台线程可以放松,直到需要更新
  • 前台线程不会"备份"更新
  • 如果前台线程是"休息",那么后台线程得到更多的处理器时间
  • Timer的频率可以设置为一个"合理"的值,比如250ms(每秒4次更新),这样进程是平稳的,但不占用整个处理器
与往常一样,在线程间通信进程时,线程安全非常重要。在此场景中,使用简单的32位int值是线程安全的,但是在32位机器上使用64位double不是线程安全的。

可以基于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类

更新进度条的详细信息,请参阅下面的示例。

看来你不是唯一一个有这个问题的人。参见Amazon s3 Transferutility。上传在c#中挂起。肯特还指出:If you read in about the S3 forums you'll find many people having similar issues