C# 异步进度条更新
我正试图使用C# 异步进度条更新,c#,async-await,C#,Async Await,我正试图使用异步等待根据复制操作更新WinForm上的进度条,但进度条只会在复制函数完成后更新,然后抛出一个无法更新的异常,因为它不在同一线程上 复制函数不需要与UI交互,但进度函数需要 但是UI并没有被阻止,所以异步部分看起来像预期的那样工作,它只是在与UI线程交互,而不是 long fileProgress = 0; long totalProgress = 0; bool complete = false; CopyFileEx.CopyFileCallbackAction callba
异步等待
根据复制操作更新WinForm上的进度条,但进度条只会在复制
函数完成后更新,然后抛出一个无法更新的异常,因为它不在同一线程上
复制函数不需要与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;
});
}
当使用async/await时,我使用IProgress和Progress实现,它们抽象掉了一些回调细节 我很确定你的程序不起作用,因为它是通过Task.run()调用在后台线程中运行的,所以它不能真正访问UI线程上下文中的UI控件 查看这篇关于使用async/await报告进度的文章,我认为这会有所帮助
在您当前的实现中,如果您希望它与回调一起工作,我想我应该直接在回调方法中更新进度条,而不是检查循环中进度变量的状态,当您将UI从后台线程中取出以实际访问进度条时,它将阻止您的UI。您需要在此处使用
IProgress
:
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;
});
}
private async void startButton_单击(对象发送方,事件参数e)
{
变量进度=新进度(百分比=>
{
fileProgressBar.Value=百分比;
});
等待副本(进度);
MessageBox.Show(“完成”);
}
无效副本(i进度)
{
Task.Run(()=>
{
CopyFileEx.FileRoutines.CopyFile(新文件信息(@“C:\\U USB\Fear.rar”)、新文件信息(@“H:\Fear.rar”)、CopyFileEx.CopyFileOptions.All、回调、空、进度);
完整=正确;
});
}
您的回调方法可以报告IProgress
的进度,如:
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;
}
CopyFileEx.CopyFileCallbackAction回调(FileInfo源、FileInfo目标、对象状态、long totalFileSize、long TotalByTestTransfered、IProgress进度)
{
fileProgress=TotalByTestTransfered;
totalProgress=totalFileSize;
进度报告(转换为32(文件进度/总进度));
返回CopyFileEx.CopyFileCallbackAction.Continue;
}
你可以看看
async/await
就是在处理I/O时不阻塞线程——任何线程。在Task.Run()
(就像您在Copy()
中所做的那样)内放置阻塞I/O调用并不能避免阻塞——它只是创建了一个其他线程稍后将拾取的任务,只是发现当它点击blockingCopyFileEx.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;
}
指出
fileProgressBar.Invoke(()=>{…})代码>不编译,您需要使用委托类型,例如:fileProgressBar.Invoke(新操作(()=>{…}))代码>