我正在开发一个使用 .NET 4.5 的 ZipArchive 和 ZipFile 类的内部备份程序。到目前为止,我已经成功地压缩了不同大小和内容的文件夹,但是当压缩一个大文件夹时,用户界面会卡住,直到所有备份完成。我一直在谷歌和 SO 上寻找解决方案,但到目前为止没有运气。
这是我启动后台线程的调用:
Dispatcher.Invoke(() =>
{
var result = BackupFolder(b1.Tag.ToString(), b1.Name);
Log = result.Result ? new BackupLog(string.Format("Finished zipping '{0}.zip'", b1.Name)) : new BackupLog(string.Format("Error zipping '{0}.zip'", b1.Name));
}, DispatcherPriority.Background);
这是它调用的方法:
private async Task<bool> BackupFolder(string path, string folderName)
{
try
{
var zipPath = string.Format("{0}{1}.zip", _backupPath.Backslash(), folderName);
Log = new BackupLog(string.Format("Starting zipping '{0}.zip'", folderName));
if (File.Exists(zipPath)) // ZIP already exists, let's update the contents
{
using (var existingZip = new FileStream(zipPath, FileMode.Open))
{
using (var archive = new ZipArchive(existingZip, ZipArchiveMode.Update))
{
foreach (var file in Directory.GetFiles(path))
{
if (archive.Entries.Any(a => a.FullName.Equals(Path.GetFileName(file)))) // Check if current file exists in the ZIP
{
var zipArchiveEntry =
archive.Entries.First(a => a.FullName.Equals(Path.GetFileName(file)));
if (zipArchiveEntry != null) zipArchiveEntry.Delete(); // Remove existing file from ZIP
}
archive.CreateEntryFromFile(file, Path.GetFileName(file));
}
}
}
}
else // ZIP does not exist, let's create it
{
ZipFile.CreateFromDirectory(path, zipPath);
}
return await Task.FromResult(true);
}
catch (Exception ex)
{
Debugger.Break();
}
return await Task.FromResult(false);
}
我已经尝试过使用 Task.Run(),创建一个 BackgroundWorker 以及可能我忘记的其他一些东西来尝试让 UI 保持交互当它在后台压缩时。
有什么建议吗?谢谢。
请您参考如下方法:
您的 BackupFolder() 方法只是名称上的“AINO”:async。 async 关键字的存在是为了改变方法的语义,以便它可以使用 await 关键字。您唯一使用 await 的地方是,您正在“等待”Task 对象,该对象已初始化 处于完成状态。没有实际的异步等待在进行。
同时,您正在使用 Dispatcher.Invoke() 来执行该方法。这意味着您明确指示框架在 UI 线程中执行您的方法。这与您的 BackupFolder() 方法不是实际上异步的事实相结合意味着您在操作期间阻塞了 UI 线程。
要修复您的代码,请将 BackupFolder() 改回简单的同步方法:
private bool BackupFolder(string path, string folderName)
{
.
.
.
return true;
}
catch (Exception ex)
{
Debugger.Break();
}
return false;
}
然后不调用 Dispatcher.Invoke(),只需在它自己的 Task 中运行同步 BackupFolder() 方法:
string path = b1.Tag.ToString(), folderName = b1.Name;
bool result = await Task.Run(() => BackupFolder(path, folderName));
注意:
- 我在上面比较保守,并假设
b1对象,无论它是什么,不是线程安全的。因此,我添加了用于从b1对象捕获当前值的局部变量,以便这些值可以安全地传递给BackupFolder()方法它最终在任务线程中执行。 - 当然,在这里使用
await运算符需要将包含该调用的方法标记为async。这可能反过来要求 that 方法的调用者被标记为async,等等。async就是这样。
