Async Progress Bar Update
When using async/await I use the IProgress and Progress implementations, which abstract away some of the callback details.
Im pretty sure what you have there isn't working because it's being run in a background thread via the Task.Run() call, so it can't really access the UI controls which are in the UI thread context.
Check out this article on reporting progress with async/await, I think it will help out.
http://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html
In your current implementation if you wanted it to work with the callback I think I would just update the progress bar directly in your callback method instead of checking status of the progress variables in a loop, which is going to block your UI when you take it out of the background thread in order to actually access the progress bar.
async / await
is all about not blocking a thread - any thread - when dealing with I/O. Putting a blocking I/O call insideTask.Run()
(like you did inCopy()
) doesn't avoid blocking - it just create a Task which some other thread will later pick up, just to find it itself gets blocked when it hits the blockingCopyFileEx.FileRoutines.CopyFile()
method.- You are getting that error because you are not using
async / await
properly (regardless the above). Think about which thread is trying to modify the UI objectfileProgressBar
: the random threadpool thread that picks up the Task you create onTask.Run()
gets to executefileProgressBar.Value = ...
, which will obviously throw.
This is one way to avoid this situation:
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;
}
You need to use IProgress<T>
here :
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;
});
}
and your callback method can report the progress of IProgress<T>
like:
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;
}
You can look at this very good article by Stephen Cleary