异步进度条更新

时间:2016-03-31 18:09:00

标签: c# async-await

我正在尝试使用async await根据复制操作更新WinForm上的进度条,但进度条只会在Copy函数完成后更新,然后抛出例外情况是它不能更新,因为它不在同一个线程上?

复制功能不需要与UI进行交互,但进程功能可以。

虽然UI没有被阻止,但是异步部分似乎正在按预期工作,它只是与不是的UI线程进行交互。

long fileProgress = 0;
long totalProgress = 0;
bool complete = false;

CopyFileEx.CopyFileCallbackAction callback(FileInfo source, FileInfo destination, object state, long totalFileSize, long totalBytesTransferred)
{
      fileProgress = totalBytesTransferred;
      totalProgress = totalFileSize;
      return CopyFileEx.CopyFileCallbackAction.Continue;
}

async Task Progress()
{
      await Task.Run(() =>
      {
           while (!complete)
           {
                if (fileProgress != 0 && totalProgress != 0)
                {
                     fileProgressBar.Value = (int)(fileProgress / totalProgress) * 100;
                }
           }
      });
}

private async void startButton_Click(object sender, EventArgs e)
{
      Copy();
      await Progress();
      MessageBox.Show("Done");
}

void Copy()
{
      Task.Run(() =>
      {
           CopyFileEx.FileRoutines.CopyFile(new FileInfo(@"C:\_USB\Fear.rar"), new FileInfo(@"H:\Fear.rar"), CopyFileEx.CopyFileOptions.All, callback, null);
           complete = true;
      });
}

3 个答案:

答案 0 :(得分:6)

您需要在此处使用IProgress<T>

private async void startButton_Click(object sender, EventArgs e)
{
      var progress = new Progress<int>(percent =>
      {
         fileProgressBar.Value = percent;
      });

      await Copy(progress);

      MessageBox.Show("Done");
}

void Copy(IProgress<int> progress)
{
      Task.Run(() =>
      {
           CopyFileEx.FileRoutines.CopyFile(new FileInfo(@"C:\_USB\Fear.rar"), new FileInfo(@"H:\Fear.rar"), CopyFileEx.CopyFileOptions.All, callback, null,progress);
           complete = true;
      });
}

并且您的回调方法可以报告IProgress<T>的进度,如:

CopyFileEx.CopyFileCallbackAction callback(FileInfo source, FileInfo destination, object state, long totalFileSize, long totalBytesTransferred,IProgress<int> progress)
{
      fileProgress = totalBytesTransferred;
      totalProgress = totalFileSize;
      progress.Report(Convert.ToInt32(fileProgress/totalProgress));
      return CopyFileEx.CopyFileCallbackAction.Continue;
}

您可以查看this very good article by Stephen Cleary

答案 1 :(得分:4)

当使用async / await时,我使用IProgress和Progress实现,它抽象出一些回调细节。

我很确定你所拥有的东西是否有效,因为它是通过Task.Run()调用在后台线程中运行的,所以它无法真正访问UI控件在UI线程上下文中。

查看这篇关于使用async / await报告进度的文章,我认为它会有所帮助。

http://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html

在您当前的实现中,如果您希望它与回调一起工作,我想我只是直接在回调方法中更新进度条,而不是在循环中检查进度变量的状态,这将阻止您的UI你把它从后台线程中删除,以便实际访问进度条。

答案 2 :(得分:4)

  1. async / await就是在处理I / O时没有阻塞线程 - 任何线程。在Task.Run()中设置阻止 I / O调用(就像在Copy()中所做的那样)并不会阻止阻塞 - 它只是创建一个任务,其他一些线程将在以后执行选择,只是为了发现它本身在遇到阻塞CopyFileEx.FileRoutines.CopyFile()方法时被阻止。
  2. 您收到该错误是因为您未正确使用async / await(无论上述情况如何)。考虑哪个线程正在尝试修改UI对象fileProgressBar:选择您在Task.Run()上创建的任务的随机线程池线程将执行fileProgressBar.Value = ...,这显然会抛出。
  3. 这是避免这种情况的一种方法:

    async Task Progress()
    {
          await Task.Run(() =>
          {
               //A random threadpool thread executes the following:
               while (!complete)
               {
                    if (fileProgress != 0 && totalProgress != 0)
                    { 
                        //Here you signal the UI thread to execute the action:
                        fileProgressBar.Invoke(new Action(() => 
                        { 
                            //This is done by the UI thread:
                            fileProgressBar.Value = (int)(fileProgress / totalProgress) * 100 
                        }));
                    }
               }
          });
    }
    
    private async void startButton_Click(object sender, EventArgs e)
    {
          await Copy();
          await Progress();
          MessageBox.Show("Done");  //here we're on the UI thread.
    }
    
    async Task Copy()
    {
        //You need find an async API for file copy, and System.IO has a lot to offer.
        //Also, there is no reason to create a Task for MyAsyncFileCopyMethod - the UI
        // will not wait (blocked) for the operation to complete if you use await:
        await MyAsyncFileCopyMethod();
        complete = true;
    }