C# 将同步zip操作转换为异步
我们得到了一个现有的库,其中一些方法需要转换为异步方法C# 将同步zip操作转换为异步,c#,asynchronous,async-await,task,C#,Asynchronous,Async Await,Task,我们得到了一个现有的库,其中一些方法需要转换为异步方法 public static bool ZipAndSaveFile(string fileToPack, string archiveName, string outputDirectory) { var archiveNameAndPath = Path.Combine(outputDirectory, archiveName); using (var zip = new ZipFile()) {
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;
}
但是,我不确定如何使用以下方法(errorhandling已被删除)。该方法的目的是压缩文件并将其保存到磁盘。(请注意,zip类不公开任何异步方法。)
实现可以如下所示:
public static async Task<bool> 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;
}
publicstaticasync任务ZipAndSaveFileAsync(stringfiletopack、stringarchivename、stringoutputdirectory)
{
var archiveNameAndPath=Path.Combine(输出目录,archiveName);
等待任务。运行(()=>
{
使用(var zip=new ZipFile())
{
zip.CompressionLevel=Ionic.Zlib.CompressionLevel.BestCompression;
zip.Comment=$“此存档文件是在{System.DateTime.UtcNow.ToString(“G”)}(UTC)”创建的;
zip.AddFile(fileToPack);
zip.Save(archiveNameAndPath);
}
});
返回true;
}
这似乎是错误的。客户端可以使用Task.Run调用sync方法
请告诉我,有人知道如何将其转换为异步方法吗?如果压缩后可以从Zip库中获取二进制数据,则使用.NET IO库来保存文件,而不是使用此库来保存文件
public static Task<bool> 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;
});
}
编辑:
对于CPU绑定的操作(如压缩),使用异步是没有意义的。在您的情况下,异步的唯一好处是将文件保存到磁盘上。我想这就是你想要的。一些答案表明压缩文件不是一个你应该异步完成的过程。我不同意这一点 我可以想象压缩文件是一个可能需要一些时间的过程。在此期间,您希望保持UI的响应性,或者希望同时压缩多个文件,或者希望在压缩下一个文件的同时上载一个压缩文件/ 显示的代码是使函数异步的正确方法。您怀疑创建这样一个小方法是否有用。为什么不让用户调用Task.Run而不是调用异步函数 其原因称为信息隐藏。通过创建隐藏的异步函数how可以异步压缩,从而使其他人不知道如何进行压缩 此外,信息隐藏使您可以自由地更改过程的内部内容,只要您不更改前置和后置条件 其中一个答案说您的函数仍然不是异步的。事实并非如此。函数的调用者可以在不等待的情况下调用异步函数。当任务正在压缩时,调用方可能会执行其他操作。只要它需要任务的布尔结果,if就可以等待任务 用法示例:
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.wheall之间的区别
在async中-等待您使用Task.WhenAll。返回是一项任务,因此您可以
await Task.WhenAll (...)
为了实现正确的异步等待,所有调用任何异步函数的函数本身都需要异步,并返回一个任务(而不是void)或任务而不是TResult。有一个例外:事件处理程序可能返回void
private async void OnButton1_clicked(object sender, ...)
{
bool zipResult = await SaveAndZipFileAsync(...);
ProcessZipResult(zipResult);
}
使用此方法,您的UI将保持响应速度。您不必调用任务。运行
如果您有一个非异步函数,并且希望在执行其他操作时开始压缩,则非异步函数必须调用Task.Run。由于函数不是异步的,所以不能使用wait。当它需要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);
}
这似乎是错误的。客户端可以调用sync方法
使用Task.Run
恰到好处。通过在Task.Run()中包装同步代码,库现在正在使用客户机的线程池资源,而不会轻易显示出来。想象一下,如果所有库都采用这种方法,客户端的线程池会发生什么情况?长话短说,只需公开同步方法,并让客户端决定是否将其包装在Task.Run()中
话虽如此,如果ZipFile对象具有异步功能(例如,具有SaveAsync()方法),那么您也可以使外部方法异步。下面是一个这样的例子:
public static async Task<bool> 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;
}
publicstaticasync任务ZipAndSaveFileAsync(stringfiletopack、stringarchivename、stringoutputdirectory)
{
var archiveNameAndPath=Path.Combine(输出目录,archiveName);
使用(var zip=new ZipFile())
{
//做事
等待zip.SaveAsync(archiveNameAndPath);
}
返回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<bool> 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;
}
publicstaticasync任务ZipAndSaveFileAsync(stringfiletopack、stringarchivename、stringoutputdirectory)
{
var archiveNameAndPath=Path.Combine(输出目录,archiveName);
使用(var zip=new ZipFile())
{
...
wait zip.SaveAsync(archiveNameAndPath).confugureaway(false);
}
返回true;
}
实现同步任务不会违反任何规定(谈论Task.FromResult)
提交请求以请求由于IO操作而在库中提供异步支持
希望最终完成,然后您可以升级应用程序中的Ionic.Zlib,删除ZipFileExtensions
,并继续使用异步版本的Save方法(这次内置到库中)
或者,您可以从GitHub克隆repo,并自己添加SaveAsync,提交回拉请求
如果库不支持同步方法,则无法将其“转换”为异步方法
从性能的角度来看,这可能不是最好的解决方案,但从管理的角度来看,您可以将“将所有内容转换为异步”和“通过使用Ionic.Zlib async提高应用程序性能”这两个故事分离开来,这会使您的积压工作更细粒度 压缩文件是一个CPU限制的操作
public static async Task<bool> 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;
}