我正在尝试使用async await基于复制操作来更新WinForm上的进度条,但进度条只会在Copy函数完成时更新,然后抛出一个无法更新的异常,因为它不在同一线程上?
复制功能不需要与UI交互,但Progress功能需要与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;
});
}发布于 2016-04-01 02:30:45
async / await就是在处理I/O时不阻塞线程--任何线程。把一个阻塞的 I/O调用放在Task.Run()中(就像你在Copy()中做的那样)并不能避免阻塞--它只是创建了一个任务,其他线程稍后会接过这个任务,结果发现当它遇到阻塞的CopyFileEx.FileRoutines.CopyFile()方法时,它自己也被阻塞了。async / await (不管上面的情况)。想一想哪个线程正在试图修改UI对象fileProgressBar:随机线程池线程选择您在Task.Run()上创建的任务来执行fileProgressBar.Value = ...,这显然会抛出。这是避免这种情况的一种方法:
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;
}发布于 2016-04-01 02:30:29
您需要在此处使用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
发布于 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。
https://stackoverflow.com/questions/36340639
复制相似问题