首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >异步进度条更新

异步进度条更新
EN

Stack Overflow用户
提问于 2016-04-01 02:09:00
回答 3查看 35.8K关注 0票数 10

我正在尝试使用async await基于复制操作来更新WinForm上的进度条,但进度条只会在Copy函数完成时更新,然后抛出一个无法更新的异常,因为它不在同一线程上?

复制功能不需要与UI交互,但Progress功能需要与UI交互。

但是UI并没有被阻塞,所以看起来异步部分像预期的那样工作,它只是与UI线程交互,而不是这样。

代码语言:javascript
复制
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;
      });
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-04-01 02:30:45

  1. async / await就是在处理I/O时不阻塞线程--任何线程。把一个阻塞的 I/O调用放在Task.Run()中(就像你在Copy()中做的那样)并不能避免阻塞--它只是创建了一个任务,其他线程稍后会接过这个任务,结果发现当它遇到阻塞的CopyFileEx.FileRoutines.CopyFile()方法时,它自己也被阻塞了。
  2. 你得到这个错误是因为你没有正确地使用async / await (不管上面的情况)。想一想哪个线程正在试图修改UI对象fileProgressBar:随机线程池线程选择您在Task.Run()上创建的任务来执行fileProgressBar.Value = ...,这显然会抛出。

这是避免这种情况的一种方法:

代码语言:javascript
复制
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;
}
票数 7
EN

Stack Overflow用户

发布于 2016-04-01 02:30:29

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

代码语言:javascript
复制
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>的进度:

代码语言:javascript
复制
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

票数 13
EN

Stack Overflow用户

发布于 2016-04-01 02:28:29

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

我非常确定你有什么是不能工作的,因为它是通过Task.Run()调用在后台线程中运行的,所以它不能真正访问UI线程上下文中的UI控件。

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

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

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36340639

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档