将同步zip操作转换为异步
我们有一个现有的库,其中一些方法需要转换为异步方法。
但是我不确定如何使用以下方法(error handling已被删除)。 该方法的目的是压缩文件并将其保存到磁盘。 (请注意,zip类不会公开任何异步方法。)
public static bool ZipAndSaveFile(string fileToPack, string archiveName, string outputDirectory) { var archiveNameAndPath = Path.Combine(outputDirectory, archiveName); using (var zip = new ZipFile()) { zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip.Comment = $"This archive was created at {System.DateTime.UtcNow.ToString("G")} (UTC)"; zip.AddFile(fileToPack); zip.Save(archiveNameAndPath); } return true; }
实现可能如下所示:
public static async Task ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory) { var archiveNameAndPath = Path.Combine(outputDirectory, archiveName); await Task.Run(() => { using (var zip = new ZipFile()) { zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip.Comment = $"This archive was created at {System.DateTime.UtcNow.ToString("G")} (UTC)"; zip.AddFile(fileToPack); zip.Save(archiveNameAndPath); } }); return true; }
这似乎是错的。 客户端可以使用Task.Run调用sync方法
请问,任何人都有关于如何将其转换为异步方法的任何提示?
这似乎是错的。 客户端可以使用Task.Run调用sync方法
发现。 通过在Task.Run()中包装同步代码,库现在正在使用客户端的线程池资源,而不是很明显。 想象一下,如果所有库采用这种方法,客户端的线程池会发生什么? 简而言之,只需公开同步方法,让客户决定是否要将其包装在Task.Run()中。
话虽如此,如果ZipFile对象具有异步function(例如,有一个SaveAsync()方法),那么你也可以使外部方法异步。 这是一个如何看起来的例子:
public static async Task ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory) { var archiveNameAndPath = Path.Combine(outputDirectory, archiveName); using (var zip = new ZipFile()) { // do stuff await zip.SaveAsync(archiveNameAndPath); } return true; }
作为临时解决方案,我将介绍一种扩展方法:
public static class ZipFileExtensions { public static Task SaveAsync(this ZipFile zipFile, string filePath) { zipFile.Save(filePath); return Task.FromResult(true); } }
然后用法是:
public static async Task ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory) { var archiveNameAndPath = Path.Combine(outputDirectory, archiveName); using (var zip = new ZipFile()) { ... await zip.SaveAsync(archiveNameAndPath).ConfugureAwait(false); } return true; }
- 实现同步任务不违反任何事情(谈论Task.FromResult)
- 向https://github.com/jstedfast/Ionic.Zlib提交请求,要求在IO操作期间在库中提供异步支持
- 希望最终完成,然后你可以升级你的应用程序中的
ZipFileExtensions
,删除ZipFileExtensions
,并继续使用Save方法的异步版本(这次内置到库中)。 - 或者,您可以从GitHub克隆repo,并自己添加SaveAsync,然后提交拉取请求。
- 如果库不支持同步方法,则无法将同步方法“转换”为异步。
从性能角度来看,这可能不是最好的解决方案,但从管理的角度来看,您可以将故事“将所有内容转换为异步”和“通过Ionic.Zlib async提高应用程序性能”,这使得您的待办事项更加精细。
public static Task ZipAndSaveFileAsync(string fileToPack, string archiveName, string outputDirectory) { return Task.Run(() => { var archiveNameAndPath = Path.Combine(outputDirectory, archiveName); using (var zip = new ZipFile()) { zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip.Comment = $"This archive was created at {System.DateTime.UtcNow.ToString("G")} (UTC)"; zip.AddFile(fileToPack); zip.Save(archiveNameAndPath); } return true; }); }
然后像这样使用
public async Task MyMethod() { bool b = await ZipAndSaveFileAsync(); }
一些答案表明,压缩文件不是您应该异步执行的过程。 我不同意这一点。
我可以想象,压缩文件是一个可能需要一些时间的过程。 在此期间,您希望保持UI响应,或者您想要同时压缩多个文件,或者您希望上传压缩文件,同时压缩下一个/
您展示的代码是使您的函数异步的正确方法。 您怀疑创建这样一个小方法是否有用。 为什么不让用户调用Task.Run而不是调用你的异步函数?
其原因称为信息隐藏。 通过创建异步function,您将隐藏异步压缩的方式,从而使其他人不知道如何执行此操作。
此外,只要您不更改前置条件和后置条件,信息隐藏就可以自由更改过程的内部。
其中一个答案说你的function仍然不是异步的。 事实并非如此。 您的函数的调用者可以在不等待它的情况下调用您的异步函数。 虽然任务是拉链,但调用者可能会做其他事情。 只要它需要任务的布尔结果,如果可以等待任务。
用法示例:
private async Task DoSomethingSimultaneously() { var taskZipFileA = ZipAndSaveFileAsync(fileA, ...) // while this task is zipping do other things, // for instance start zipping file B: var taskZipFileB = ZipAndSaveFileAsync(fileB, ...) // while both tasks are zipping do other things // after a while you want to wait until both files are finished: await Task.WhenAll(new Task[] {taskZipFileA, taskZipFileB}); // after the await, the results are known: if (taskZipFileA.Result) { // process the boolean result of taskZipFile A }
注意Task.WaitAll和Task.WhenAll在异步中的区别 – 等待你使用Task.WhenAll。 返回是一个任务,所以你可以
await Task.WhenAll (...)
对于正确的async-await,调用任何异步函数的所有函数都需要自己异步并返回Task(而不是void)或Task
private async void OnButton1_clicked(object sender, ...) { bool zipResult = await SaveAndZipFileAsync(...); ProcessZipResult(zipResult); }
使用此方法,您的UI可以保持响应。 您不必调用Task.Run
如果你有一个非异步函数并且想要在执行其他操作时开始压缩,那么非异步函数必须调用Task.Run。 由于该函数不是异步,因此无法使用等待。 当它需要task.Run的结果时,它需要使用Task.Wait或Task.WaitAll
private void NonAsyncZipper() { var taskZipFileA = Task.Run ( () => ZipAndSaveFileAsync(...); // while zipping do other things // after a while when the result is needed: taskZipFileA.Wait(); ProcesZipResult(taskZipFileA.Result); }
如果在压缩后可以从Zip库中获取二进制数据,那么使用.NET IO库来保存文件,而不是使用此库来保存文件。
编辑:
使用异步进行CPU绑定操作(例如压缩)是没有意义的。 在您的情况下,您可以从异步获得的唯一好处是将文件保存到磁盘。 我以为这就是你所要求的。