将同步zip操作转换为异步

时间:2015-08-06 09:43:47

标签: c# asynchronous async-await task

我们有一个现有的库,其中一些方法需要转换为异步方法。

但是我不确定如何使用以下方法执行此操作(错误处理已被删除)。该方法的目的是压缩文件并将其保存到磁盘。 (请注意,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<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;
}

这似乎不对。客户端可以使用Task.Run

调用sync方法

请问,任何人都有关于如何将其转换为异步方法的任何提示吗?

5 个答案:

答案 0 :(得分:1)

  

这似乎不对。客户端可以调用sync方法   使用Task.Run

点亮。通过在Task.Run()中包装同步代码,库现在正在使用客户端的线程池资源,而不是很明显。想象一下,如果所有库采用这种方法,客户端的线程池会发生什么?简而言之,只需公开同步方法,让客户决定是否要将其包装在Task.Run()中。

话虽如此,如果ZipFile对象具有异步功能(例如具有SaveAsync()方法),那么您也可以使外部方法异步。这是一个看起来如何的例子:

(s3/get-object :bucket-name "my-bucket" :key "foo"
               :sdk-client-execution-timeout 10000)

答案 1 :(得分:1)

作为临时解决方案,我将介绍一种扩展方法:

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;
 }
  1. 实现同步任务不违反任何事情(谈论Task.FromResult)
  2. https://github.com/jstedfast/Ionic.Zlib提交请求,要求因IO操作而在库中提供异步支持
  3. 希望最终完成,然后您可以升级应用中的Ionic.Zlib,删除ZipFileExtensions,然后继续使用Save方法的异步版本(这次内置到库中)
  4. 或者,您可以从GitHub克隆回购,并自行添加SaveAsync,提交回拉请求。
  5. 只是不可能转换&#39;如果库不支持异步,则为异步的同步方法。
  6. 从性能角度来看,这可能不是最好的解决方案,但从管理的角度来看,您可以将故事分解为#34;将所有内容转换为异步&#34;和#34;通过Ionic.Zlib async&#34;提高应用程序性能,是什么让您的待办事项更精细。

答案 2 :(得分:-1)

 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;
        });

    }

然后像这样使用

public async Task MyMethod() 
{
   bool b = await ZipAndSaveFileAsync();
}

答案 3 :(得分:-1)

有些答案表明,压缩文件不是一个异步进行的过程。我不同意这一点。

我可以想象,压缩文件是一个可能需要一些时间的过程。在此期间,您希望保持UI响应,或者您想要同时压缩多个文件,或者您希望上传压缩文件,同时压缩下一个/

您展示的代码是使您的函数异步的正确方法。您怀疑创建这样一个小方法是否有用。为什么不让用户调用Task.Run而不是调用你的异步函数?

其原因称为信息隐藏。通过创建异步功能,您隐藏如何以异步方式压缩,从而使其他人不知道如何执行此操作。

此外,只要你不改变前后条件,信息隐藏就可以自由地改变程序的内部。

其中一个答案说您的功能仍然不是异步的。事实并非如此。您的函数的调用者可以在不等待的情况下调用您的异步函数。当任务是拉链时,调用者可能会做其他事情。只要它需要任务的布尔结果就可以等待任务。

使用示例:

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 <TResult&gt;而不是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);
 }

答案 4 :(得分:-2)

如果在压缩后可以从Zip库中获取二进制数据,那么使用.NET IO库来保存文件,而不是使用此库来保存文件。

编辑:

对CPU绑定操作(例如压缩)使用async毫无意义。在您的情况下,您可以从异步中获得的唯一好处是将文件保存到磁盘。我认为这就是你所要求的。