我正在尝试使用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;
});
}
答案 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;
}
答案 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)
async / await
就是在处理I / O时没有阻塞线程 - 任何线程。在Task.Run()
中设置阻止 I / O调用(就像在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;
}