将同步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; } 
  1. 实现同步任务不违反任何事情(谈论Task.FromResult)
  2. 向https://github.com/jstedfast/Ionic.Zlib提交请求,要求在IO操作期间在库中提供异步支持
  3. 希望最终完成,然后你可以升级你的应用程序中的ZipFileExtensions ,删除ZipFileExtensions ,并继续使用Save方法的异步版本(这次内置到库中)。
  4. 或者,您可以从GitHub克隆repo,并自己添加SaveAsync,然后提交拉取请求。
  5. 如果库不支持同步方法,则无法将同步方法“转换”为异步。

从性能角度来看,这可能不是最好的解决方案,但从管理的角度来看,您可以将故事“将所有内容转换为异步”和“通过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.WaitAllTask.WhenAll在异步中的区别 – 等待你使用Task.WhenAll。 返回是一个任务,所以你可以

 await Task.WhenAll (...) 

对于正确的async-await,调用任何异步函数的所有函数都需要自己异步并返回Task(而不是void)或Task >而不是TResult。 有一个例外:事件处理程序可能返回void。

 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绑定操作(例如压缩)是没有意义的。 在您的情况下,您可以从异步获得的唯一好处是将文件保存到磁盘。 我以为这就是你所要求的。