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.WaitAllTask.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;
     }