【问题标题】:Asynchronous SHA256 Hashing异步 SHA256 散列
【发布时间】:2015-01-23 20:44:20
【问题描述】:

我有以下方法:

public static string Sha256Hash(string input) {
    if(String.IsNullOrEmpty(input)) return String.Empty;
    using(HashAlgorithm algorithm = new SHA256CryptoServiceProvider()) {
        byte[] inputBytes = Encoding.UTF8.GetBytes(input);
        byte[] hashBytes = algorithm.ComputeHash(inputBytes);
        return BitConverter.ToString(hashBytes).Replace("-", String.Empty);
    }
}

有没有办法让它异步?我希望使用 asyncawait 关键字,但 HashAlgorithm 类不为此提供任何异步支持。

另一种方法是将所有逻辑封装在一个:

public static async string Sha256Hash(string input) {
     return await Task.Run(() => {
         //Hashing here...
     });
}

但这似乎并不干净,我不确定它是否是异步执行操作的正确(或有效)方式。

我该怎么做才能做到这一点?

【问题讨论】:

  • 您为什么要尝试异步执行此操作?
  • @CoryNelson 老实说,我不知道。我认为我会通过使其异步运行或在另一个线程上运行来优化代码。但答案让我清醒了。
  • @CoryNelson:我最近遇到了一种情况,我需要异步执行此操作,以便在计算大文件的哈希值时保持响应式 UI。

标签: c# .net asynchronous async-await sha256


【解决方案1】:

正如其他回答者所说,散列是一项受 CPU 限制的活动,因此它没有您可以调用的异步方法。但是,您可以通过逐块异步读取文件,然后对从文件中读取的字节进行哈希处理,使您的哈希方法异步。哈希将同步完成,但读取将是异步的,因此您的整个方法将是异步的。

这是实现我刚才描述的目的的示例代码。

public static async Threading.Tasks.Task<string> GetHashAsync<T>(this Stream stream) 
    where T : HashAlgorithm, new()
{
    StringBuilder sb;

    using (var algo = new T())
    {
        var buffer = new byte[8192];
        int bytesRead;

        // compute the hash on 8KiB blocks
        while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
            algo.TransformBlock(buffer, 0, bytesRead, buffer, 0);
        algo.TransformFinalBlock(buffer, 0, bytesRead);

        // build the hash string
        sb = new StringBuilder(algo.HashSize / 4);
        foreach (var b in algo.Hash)
            sb.AppendFormat("{0:x2}", b);
    }

    return sb?.ToString();
}

函数可以这样调用

using (var stream = System.IO.File.OpenRead(@"C:\path\to\file.txt"))
    string sha256 = await stream.GetHashAsync<SHA256CryptoServiceProvider>();

当然,您同样可以使用其他哈希算法如SHA1CryptoServiceProviderSHA512CryptoServiceProvider 作为泛型类型参数调用该方法。

同样,通过一些修改,您还可以让它根据您的情况散列一个字符串。

【讨论】:

  • 谢谢!两件小事:最后不需要对 sb 进行空检查,最好在此类代码上使用 ConfigureAwait(false)。
  • @Guillaume 你在这两个方面都是对的。然而,我将空检查放在那里以满足过分热心的代码分析器。如果它适合你,你可以删除它。是的,建议使用ConfigureAwait(false)
  • 您的代码假定如果Stream.ReadAsync 返回的值小于buffer.Length,则它已到达末尾 - 但Stream.ReadAsync 的文档说只有零返回值表示到达 EOF。
  • @Dai:感谢您发现这一疏忽。现在已经更正了。
【解决方案2】:

异步运行(使用 Task.Run)的开销可能会高于同步运行。

异步接口不可用,因为它是 CPU 绑定操作。正如您所指出的,您可以使其异步(使用 Task.Run),但我建议不要这样做。

【讨论】:

    【解决方案3】:

    您所做的工作本质上是同步的 CPU 密集型工作。它本身并不像网络 IO 那样是异步的。如果您想在另一个线程中运行一些同步的 CPU 绑定工作并异步等待它完成,那么Task.Run 确实是完成此任务的合适工具,假设该操作运行时间足够长以至于需要异步执行它。

    That said, there really isn't any reason to expose an asynchronous wrapper over your synchronous method. 通常只同步公开该方法更有意义,如果特定调用者需要它在另一个线程中异步运行,他们可以使用Task.Run 明确指示该特定调用的需要。

    【讨论】:

    • 那我会听从你的建议的。并让调用者决定包装器是否方便
    猜你喜欢
    • 1970-01-01
    • 2021-05-21
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 2014-01-26
    • 2011-08-10
    • 2020-05-31
    • 2015-12-14
    相关资源
    最近更新 更多